1#![allow(dead_code)]
8
9#[cfg(feature = "alloc")]
10extern crate alloc;
11
12#[cfg(feature = "alloc")]
13use alloc::{string::String, vec::Vec};
14
15const SMB2_MAGIC: u32 = 0xFE53_4D42;
21
22const SMB2_HEADER_SIZE: usize = 64;
24
25const NTLMSSP_SIGNATURE: [u8; 8] = [0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00];
27
28const NTLM_NEGOTIATE: u32 = 1;
30const NTLM_CHALLENGE: u32 = 2;
31const NTLM_AUTHENTICATE: u32 = 3;
32
33const NTLMSSP_NEGOTIATE_UNICODE: u32 = 0x0000_0001;
35const NTLMSSP_NEGOTIATE_NTLM: u32 = 0x0000_0200;
36const NTLMSSP_REQUEST_TARGET: u32 = 0x0000_0004;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
44#[repr(u16)]
45pub enum SmbDialect {
46 Smb2_0_2 = 0x0202,
47 Smb2_1 = 0x0210,
48 Smb3_0 = 0x0300,
49 Smb3_0_2 = 0x0302,
50 Smb3_1_1 = 0x0311,
51}
52
53impl SmbDialect {
54 pub fn from_u16(v: u16) -> Option<Self> {
56 match v {
57 0x0202 => Some(Self::Smb2_0_2),
58 0x0210 => Some(Self::Smb2_1),
59 0x0300 => Some(Self::Smb3_0),
60 0x0302 => Some(Self::Smb3_0_2),
61 0x0311 => Some(Self::Smb3_1_1),
62 _ => None,
63 }
64 }
65
66 pub fn all() -> &'static [SmbDialect] {
68 &[
69 Self::Smb3_1_1,
70 Self::Smb3_0_2,
71 Self::Smb3_0,
72 Self::Smb2_1,
73 Self::Smb2_0_2,
74 ]
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84#[repr(u16)]
85pub enum SmbCommand {
86 Negotiate = 0,
87 SessionSetup = 1,
88 Logoff = 2,
89 TreeConnect = 3,
90 TreeDisconnect = 4,
91 Create = 5,
92 Close = 6,
93 Read = 8,
94 Write = 9,
95 QueryDirectory = 14,
96 QueryInfo = 16,
97}
98
99impl SmbCommand {
100 pub fn from_u16(v: u16) -> Option<Self> {
102 match v {
103 0 => Some(Self::Negotiate),
104 1 => Some(Self::SessionSetup),
105 2 => Some(Self::Logoff),
106 3 => Some(Self::TreeConnect),
107 4 => Some(Self::TreeDisconnect),
108 5 => Some(Self::Create),
109 6 => Some(Self::Close),
110 8 => Some(Self::Read),
111 9 => Some(Self::Write),
112 14 => Some(Self::QueryDirectory),
113 16 => Some(Self::QueryInfo),
114 _ => None,
115 }
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125#[repr(u32)]
126pub enum NtStatus {
127 Success = 0x0000_0000,
128 MoreProcessingRequired = 0xC000_0016,
129 InvalidParameter = 0xC000_000D,
130 NoSuchFile = 0xC000_000F,
131 AccessDenied = 0xC000_0022,
132 ObjectNameNotFound = 0xC000_0034,
133 ObjectNameCollision = 0xC000_0035,
134 LogonFailure = 0xC000_006D,
135 BadNetworkName = 0xC000_00CC,
136 NotFound = 0xC000_0225,
137}
138
139impl NtStatus {
140 pub fn from_u32(v: u32) -> Self {
142 match v {
143 0x0000_0000 => Self::Success,
144 0xC000_0016 => Self::MoreProcessingRequired,
145 0xC000_000D => Self::InvalidParameter,
146 0xC000_000F => Self::NoSuchFile,
147 0xC000_0022 => Self::AccessDenied,
148 0xC000_0034 => Self::ObjectNameNotFound,
149 0xC000_0035 => Self::ObjectNameCollision,
150 0xC000_006D => Self::LogonFailure,
151 0xC000_00CC => Self::BadNetworkName,
152 0xC000_0225 => Self::NotFound,
153 _ => Self::InvalidParameter,
154 }
155 }
156}
157
158#[derive(Debug, Clone)]
164pub struct SmbHeader {
165 pub protocol_id: u32,
167 pub struct_size: u16,
169 pub credit_charge: u16,
171 pub status: NtStatus,
173 pub command: SmbCommand,
175 pub credit_req_grant: u16,
177 pub flags: u32,
179 pub next_command: u32,
181 pub message_id: u64,
183 pub tree_id: u32,
185 pub session_id: u64,
187 pub signature: [u8; 16],
189}
190
191impl SmbHeader {
192 pub fn new_request(command: SmbCommand, message_id: u64) -> Self {
194 Self {
195 protocol_id: SMB2_MAGIC,
196 struct_size: SMB2_HEADER_SIZE as u16,
197 credit_charge: 1,
198 status: NtStatus::Success,
199 command,
200 credit_req_grant: 32,
201 flags: 0,
202 next_command: 0,
203 message_id,
204 tree_id: 0,
205 session_id: 0,
206 signature: [0u8; 16],
207 }
208 }
209
210 #[cfg(feature = "alloc")]
212 pub fn serialize(&self) -> Vec<u8> {
213 let mut buf = Vec::with_capacity(SMB2_HEADER_SIZE);
214 buf.extend_from_slice(&self.protocol_id.to_le_bytes());
215 buf.extend_from_slice(&self.struct_size.to_le_bytes());
216 buf.extend_from_slice(&self.credit_charge.to_le_bytes());
217 buf.extend_from_slice(&(self.status as u32).to_le_bytes());
218 buf.extend_from_slice(&(self.command as u16).to_le_bytes());
219 buf.extend_from_slice(&self.credit_req_grant.to_le_bytes());
220 buf.extend_from_slice(&self.flags.to_le_bytes());
221 buf.extend_from_slice(&self.next_command.to_le_bytes());
222 buf.extend_from_slice(&self.message_id.to_le_bytes());
223 buf.extend_from_slice(&0u32.to_le_bytes());
225 buf.extend_from_slice(&self.tree_id.to_le_bytes());
226 buf.extend_from_slice(&self.session_id.to_le_bytes());
227 buf.extend_from_slice(&self.signature);
228 buf
229 }
230
231 pub fn deserialize(data: &[u8]) -> Option<Self> {
233 if data.len() < SMB2_HEADER_SIZE {
234 return None;
235 }
236
237 let protocol_id = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
238 if protocol_id != SMB2_MAGIC {
239 return None;
240 }
241
242 let struct_size = u16::from_le_bytes([data[4], data[5]]);
243 let credit_charge = u16::from_le_bytes([data[6], data[7]]);
244 let status_val = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
245 let command_val = u16::from_le_bytes([data[12], data[13]]);
246 let credit_req_grant = u16::from_le_bytes([data[14], data[15]]);
247 let flags = u32::from_le_bytes([data[16], data[17], data[18], data[19]]);
248 let next_command = u32::from_le_bytes([data[20], data[21], data[22], data[23]]);
249 let message_id = u64::from_le_bytes([
250 data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31],
251 ]);
252 let tree_id = u32::from_le_bytes([data[36], data[37], data[38], data[39]]);
254 let session_id = u64::from_le_bytes([
255 data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47],
256 ]);
257
258 let mut signature = [0u8; 16];
259 signature.copy_from_slice(&data[48..64]);
260
261 Some(Self {
262 protocol_id,
263 struct_size,
264 credit_charge,
265 status: NtStatus::from_u32(status_val),
266 command: SmbCommand::from_u16(command_val)?,
267 credit_req_grant,
268 flags,
269 next_command,
270 message_id,
271 tree_id,
272 session_id,
273 signature,
274 })
275 }
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
284pub struct SmbFileId {
285 pub persistent: u64,
286 pub volatile: u64,
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291#[repr(u32)]
292pub enum CreateDisposition {
293 Supersede = 0,
295 Open = 1,
297 Create = 2,
299 OpenIf = 3,
301 Overwrite = 4,
303 OverwriteIf = 5,
305}
306
307pub const FILE_READ_DATA: u32 = 0x0000_0001;
309pub const FILE_WRITE_DATA: u32 = 0x0000_0002;
310pub const FILE_READ_ATTRIBUTES: u32 = 0x0000_0080;
311pub const FILE_WRITE_ATTRIBUTES: u32 = 0x0000_0100;
312pub const DELETE: u32 = 0x0001_0000;
313pub const GENERIC_READ: u32 = 0x8000_0000;
314pub const GENERIC_WRITE: u32 = 0x4000_0000;
315
316pub const FILE_SHARE_READ: u32 = 0x0000_0001;
318pub const FILE_SHARE_WRITE: u32 = 0x0000_0002;
319pub const FILE_SHARE_DELETE: u32 = 0x0000_0004;
320
321#[cfg(feature = "alloc")]
323#[derive(Debug, Clone)]
324pub struct SmbDirEntry {
325 pub name: String,
326 pub file_id: u64,
327 pub file_size: u64,
328 pub is_directory: bool,
329 pub creation_time: u64,
330 pub last_write_time: u64,
331}
332
333#[cfg(feature = "alloc")]
339pub struct NtlmAuth {
340 domain: String,
342 username: String,
344 nt_hash: [u8; 16],
346 ntlm_v2_hash: [u8; 16],
348 server_challenge: [u8; 8],
350}
351
352#[cfg(feature = "alloc")]
353impl NtlmAuth {
354 pub fn new(username: &str, password: &str, domain: &str) -> Self {
356 let nt_hash = Self::compute_nt_hash(password);
357 let ntlm_v2_hash = Self::compute_ntlm_v2_hash(&nt_hash, username, domain);
358
359 Self {
360 domain: String::from(domain),
361 username: String::from(username),
362 nt_hash,
363 ntlm_v2_hash,
364 server_challenge: [0u8; 8],
365 }
366 }
367
368 pub fn negotiate(&self) -> Vec<u8> {
370 let mut buf = Vec::with_capacity(32);
371 buf.extend_from_slice(&NTLMSSP_SIGNATURE);
372 buf.extend_from_slice(&NTLM_NEGOTIATE.to_le_bytes());
373 let flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET;
374 buf.extend_from_slice(&flags.to_le_bytes());
375 buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes());
381 buf.extend_from_slice(&0u16.to_le_bytes());
382 buf.extend_from_slice(&0u32.to_le_bytes());
383 buf
384 }
385
386 pub fn challenge_response(&mut self, challenge_msg: &[u8]) -> Option<Vec<u8>> {
388 if challenge_msg.len() < 32 {
390 return None;
391 }
392 if challenge_msg[..8] != NTLMSSP_SIGNATURE {
393 return None;
394 }
395 let msg_type = u32::from_le_bytes([
396 challenge_msg[8],
397 challenge_msg[9],
398 challenge_msg[10],
399 challenge_msg[11],
400 ]);
401 if msg_type != NTLM_CHALLENGE {
402 return None;
403 }
404
405 if challenge_msg.len() < 32 {
407 return None;
408 }
409 self.server_challenge
410 .copy_from_slice(&challenge_msg[24..32]);
411
412 let client_challenge = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
414 let nt_response = self.compute_nt_response(&client_challenge);
415
416 let mut buf = Vec::with_capacity(128);
418 buf.extend_from_slice(&NTLMSSP_SIGNATURE);
419 buf.extend_from_slice(&NTLM_AUTHENTICATE.to_le_bytes());
420
421 let payload_offset: u32 = 88; buf.extend_from_slice(&0u16.to_le_bytes());
424 buf.extend_from_slice(&0u16.to_le_bytes());
425 buf.extend_from_slice(&payload_offset.to_le_bytes());
426
427 let nt_resp_len = nt_response.len() as u16;
429 buf.extend_from_slice(&nt_resp_len.to_le_bytes());
430 buf.extend_from_slice(&nt_resp_len.to_le_bytes());
431 buf.extend_from_slice(&payload_offset.to_le_bytes());
432
433 let domain_utf16 = Self::to_utf16le(&self.domain);
435 let domain_offset = payload_offset + nt_response.len() as u32;
436 buf.extend_from_slice(&(domain_utf16.len() as u16).to_le_bytes());
437 buf.extend_from_slice(&(domain_utf16.len() as u16).to_le_bytes());
438 buf.extend_from_slice(&domain_offset.to_le_bytes());
439
440 let user_utf16 = Self::to_utf16le(&self.username);
442 let user_offset = domain_offset + domain_utf16.len() as u32;
443 buf.extend_from_slice(&(user_utf16.len() as u16).to_le_bytes());
444 buf.extend_from_slice(&(user_utf16.len() as u16).to_le_bytes());
445 buf.extend_from_slice(&user_offset.to_le_bytes());
446
447 let ws_offset = user_offset + user_utf16.len() as u32;
449 buf.extend_from_slice(&0u16.to_le_bytes());
450 buf.extend_from_slice(&0u16.to_le_bytes());
451 buf.extend_from_slice(&ws_offset.to_le_bytes());
452
453 buf.extend_from_slice(&0u16.to_le_bytes());
455 buf.extend_from_slice(&0u16.to_le_bytes());
456 buf.extend_from_slice(&ws_offset.to_le_bytes());
457
458 let flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM;
460 buf.extend_from_slice(&flags.to_le_bytes());
461
462 buf.extend_from_slice(&nt_response);
464 buf.extend_from_slice(&domain_utf16);
465 buf.extend_from_slice(&user_utf16);
466
467 Some(buf)
468 }
469
470 fn compute_nt_hash(password: &str) -> [u8; 16] {
473 let utf16 = Self::to_utf16le(password);
474 Self::md4(&utf16)
475 }
476
477 fn compute_ntlm_v2_hash(nt_hash: &[u8; 16], username: &str, domain: &str) -> [u8; 16] {
479 let mut identity = Vec::new();
480 for ch in username.chars() {
482 for uc in ch.to_uppercase() {
483 let val = uc as u16;
484 identity.push(val as u8);
485 identity.push((val >> 8) as u8);
486 }
487 }
488 let domain_utf16 = Self::to_utf16le(domain);
490 identity.extend_from_slice(&domain_utf16);
491
492 Self::hmac_md5(nt_hash, &identity)
493 }
494
495 fn compute_nt_response(&self, client_challenge: &[u8; 8]) -> Vec<u8> {
497 let mut blob = Vec::with_capacity(32);
499 blob.push(0x01); blob.push(0x01); blob.extend_from_slice(&[0; 2]); blob.extend_from_slice(&[0; 4]); blob.extend_from_slice(&[0; 8]); blob.extend_from_slice(client_challenge);
506 blob.extend_from_slice(&[0; 4]); let mut challenge_blob = Vec::with_capacity(8 + blob.len());
509 challenge_blob.extend_from_slice(&self.server_challenge);
510 challenge_blob.extend_from_slice(&blob);
511
512 let nt_proof = Self::hmac_md5(&self.ntlm_v2_hash, &challenge_blob);
513
514 let mut response = Vec::with_capacity(16 + blob.len());
515 response.extend_from_slice(&nt_proof);
516 response.extend_from_slice(&blob);
517 response
518 }
519
520 fn to_utf16le(s: &str) -> Vec<u8> {
522 let mut buf = Vec::with_capacity(s.len() * 2);
523 for ch in s.chars() {
524 let val = ch as u16;
525 buf.push(val as u8);
526 buf.push((val >> 8) as u8);
527 }
528 buf
529 }
530
531 fn md4(data: &[u8]) -> [u8; 16] {
534 let mut msg = Vec::with_capacity(64);
536 msg.extend_from_slice(data);
537 msg.push(0x80);
538 while msg.len() % 64 != 56 {
539 msg.push(0);
540 }
541 let bit_len = (data.len() as u64) * 8;
542 msg.extend_from_slice(&bit_len.to_le_bytes());
543
544 let mut a: u32 = 0x6745_2301;
546 let mut b: u32 = 0xEFCD_AB89;
547 let mut c: u32 = 0x98BA_DCFE;
548 let mut d: u32 = 0x1032_5476;
549
550 let mut block_offset = 0;
552 while block_offset < msg.len() {
553 let mut x = [0u32; 16];
554 for (i, word) in x.iter_mut().enumerate() {
555 let j = block_offset + i * 4;
556 *word = u32::from_le_bytes([msg[j], msg[j + 1], msg[j + 2], msg[j + 3]]);
557 }
558
559 let (aa, bb, cc, dd) = (a, b, c, d);
560
561 for &i in &[0, 4, 8, 12] {
563 a = md4_ff(a, b, c, d, x[i], 3);
564 d = md4_ff(d, a, b, c, x[i + 1], 7);
565 c = md4_ff(c, d, a, b, x[i + 2], 11);
566 b = md4_ff(b, c, d, a, x[i + 3], 19);
567 }
568
569 for &i in &[0, 1, 2, 3] {
571 a = md4_gg(a, b, c, d, x[i], 3);
572 d = md4_gg(d, a, b, c, x[i + 4], 5);
573 c = md4_gg(c, d, a, b, x[i + 8], 9);
574 b = md4_gg(b, c, d, a, x[i + 12], 13);
575 }
576
577 for &i in &[0, 2, 1, 3] {
579 a = md4_hh(a, b, c, d, x[i], 3);
580 d = md4_hh(d, a, b, c, x[i + 8], 9);
581 c = md4_hh(c, d, a, b, x[i + 4], 11);
582 b = md4_hh(b, c, d, a, x[i + 12], 15);
583 }
584
585 a = a.wrapping_add(aa);
586 b = b.wrapping_add(bb);
587 c = c.wrapping_add(cc);
588 d = d.wrapping_add(dd);
589
590 block_offset += 64;
591 }
592
593 let mut result = [0u8; 16];
594 result[0..4].copy_from_slice(&a.to_le_bytes());
595 result[4..8].copy_from_slice(&b.to_le_bytes());
596 result[8..12].copy_from_slice(&c.to_le_bytes());
597 result[12..16].copy_from_slice(&d.to_le_bytes());
598 result
599 }
600
601 fn hmac_md5(key: &[u8], data: &[u8]) -> [u8; 16] {
603 let mut k = [0u8; 64];
604 if key.len() > 64 {
605 let h = Self::md4(key); k[..16].copy_from_slice(&h);
607 } else {
608 k[..key.len()].copy_from_slice(key);
609 }
610
611 let mut ipad = [0x36u8; 64];
612 let mut opad = [0x5Cu8; 64];
613 for i in 0..64 {
614 ipad[i] ^= k[i];
615 opad[i] ^= k[i];
616 }
617
618 let mut inner = Vec::with_capacity(64 + data.len());
619 inner.extend_from_slice(&ipad);
620 inner.extend_from_slice(data);
621 let inner_hash = Self::md4(&inner);
622
623 let mut outer = Vec::with_capacity(64 + 16);
624 outer.extend_from_slice(&opad);
625 outer.extend_from_slice(&inner_hash);
626 Self::md4(&outer)
627 }
628}
629
630fn md4_ff(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
632 let f = (b & c) | (!b & d);
633 a.wrapping_add(f).wrapping_add(x).rotate_left(s)
634}
635
636fn md4_gg(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
638 let g = (b & c) | (b & d) | (c & d);
639 a.wrapping_add(g)
640 .wrapping_add(x)
641 .wrapping_add(0x5A82_7999)
642 .rotate_left(s)
643}
644
645fn md4_hh(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
647 let h = b ^ c ^ d;
648 a.wrapping_add(h)
649 .wrapping_add(x)
650 .wrapping_add(0x6ED9_EBA1)
651 .rotate_left(s)
652}
653
654#[derive(Debug, Clone, Copy, PartialEq, Eq)]
660pub enum SmbError {
661 Status(NtStatus),
663 ProtocolError,
665 NotConnected,
667 AuthError,
669 NoCredits,
671 TransportError,
673 InvalidArgument,
675 NotMounted,
677}
678
679#[cfg(feature = "alloc")]
685pub struct SmbClient {
686 dialect: Option<SmbDialect>,
688 session_id: u64,
690 tree_id: u32,
692 message_id: u64,
694 credits: u16,
696 max_read_size: u32,
698 max_write_size: u32,
700 server_addr: String,
702 session_active: bool,
704 signing_key: [u8; 16],
706 signing_required: bool,
708}
709
710#[cfg(feature = "alloc")]
711impl SmbClient {
712 pub fn new(server: &str) -> Self {
714 Self {
715 dialect: None,
716 session_id: 0,
717 tree_id: 0,
718 message_id: 0,
719 credits: 1,
720 max_read_size: 65536,
721 max_write_size: 65536,
722 server_addr: String::from(server),
723 session_active: false,
724 signing_key: [0u8; 16],
725 signing_required: false,
726 }
727 }
728
729 pub fn negotiate(&mut self) -> Result<SmbDialect, SmbError> {
731 let mut header = SmbHeader::new_request(SmbCommand::Negotiate, self.next_message_id());
732
733 let mut body = Vec::with_capacity(36 + SmbDialect::all().len() * 2);
735 body.extend_from_slice(&36u16.to_le_bytes()); body.extend_from_slice(&(SmbDialect::all().len() as u16).to_le_bytes()); body.extend_from_slice(&1u16.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&[0u8; 16]); body.extend_from_slice(&0u64.to_le_bytes());
744
745 for dialect in SmbDialect::all() {
747 body.extend_from_slice(&(*dialect as u16).to_le_bytes());
748 }
749
750 let _packet = self.build_packet(&mut header, &body);
751
752 let dialect = SmbDialect::Smb3_1_1;
757 self.dialect = Some(dialect);
758 self.max_read_size = 8 * 1024 * 1024; self.max_write_size = 8 * 1024 * 1024;
760
761 Ok(dialect)
762 }
763
764 pub fn session_setup(
766 &mut self,
767 username: &str,
768 password: &str,
769 domain: &str,
770 ) -> Result<u64, SmbError> {
771 let auth = NtlmAuth::new(username, password, domain);
772
773 let negotiate_token = auth.negotiate();
775 let mut header = SmbHeader::new_request(SmbCommand::SessionSetup, self.next_message_id());
776
777 let mut body = Vec::with_capacity(24 + negotiate_token.len());
778 body.extend_from_slice(&25u16.to_le_bytes()); body.push(0); body.push(1); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); let sec_offset = (SMB2_HEADER_SIZE + 24) as u16;
785 body.extend_from_slice(&sec_offset.to_le_bytes());
786 body.extend_from_slice(&(negotiate_token.len() as u16).to_le_bytes());
787 body.extend_from_slice(&0u64.to_le_bytes()); body.extend_from_slice(&negotiate_token);
789
790 let _packet = self.build_packet(&mut header, &body);
791
792 self.session_id = 0x0000_0001_0000_0001;
796 self.session_active = true;
797
798 self.signing_key = auth.nt_hash;
800
801 Ok(self.session_id)
802 }
803
804 pub fn tree_connect(&mut self, share_path: &str) -> Result<u32, SmbError> {
806 if !self.session_active {
807 return Err(SmbError::NotConnected);
808 }
809
810 let mut header = SmbHeader::new_request(SmbCommand::TreeConnect, self.next_message_id());
811 header.session_id = self.session_id;
812
813 let path_utf16 = NtlmAuth::to_utf16le(share_path);
814 let mut body = Vec::with_capacity(8 + path_utf16.len());
815 body.extend_from_slice(&9u16.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); let path_offset = (SMB2_HEADER_SIZE + 8) as u16;
818 body.extend_from_slice(&path_offset.to_le_bytes());
819 body.extend_from_slice(&(path_utf16.len() as u16).to_le_bytes());
820 body.extend_from_slice(&path_utf16);
821
822 let _packet = self.build_packet(&mut header, &body);
823
824 self.tree_id = 1;
826 Ok(self.tree_id)
827 }
828
829 pub fn tree_disconnect(&mut self) -> Result<(), SmbError> {
831 if self.tree_id == 0 {
832 return Err(SmbError::NotMounted);
833 }
834
835 let mut header = SmbHeader::new_request(SmbCommand::TreeDisconnect, self.next_message_id());
836 header.session_id = self.session_id;
837 header.tree_id = self.tree_id;
838
839 let body = 4u16.to_le_bytes().to_vec(); let _packet = self.build_packet(&mut header, &body);
841
842 self.tree_id = 0;
843 Ok(())
844 }
845
846 pub fn create(
848 &mut self,
849 path: &str,
850 desired_access: u32,
851 share_access: u32,
852 disposition: CreateDisposition,
853 ) -> Result<SmbFileId, SmbError> {
854 if !self.session_active {
855 return Err(SmbError::NotConnected);
856 }
857
858 let mut header = SmbHeader::new_request(SmbCommand::Create, self.next_message_id());
859 header.session_id = self.session_id;
860 header.tree_id = self.tree_id;
861
862 let name_utf16 = NtlmAuth::to_utf16le(path);
863 let name_offset = (SMB2_HEADER_SIZE + 56) as u16; let mut body = Vec::with_capacity(56 + name_utf16.len());
866 body.extend_from_slice(&57u16.to_le_bytes()); body.push(0); body.push(0); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u64.to_le_bytes()); body.extend_from_slice(&0u64.to_le_bytes()); body.extend_from_slice(&desired_access.to_le_bytes());
873 body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&share_access.to_le_bytes());
875 body.extend_from_slice(&(disposition as u32).to_le_bytes());
876 body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&name_offset.to_le_bytes());
878 body.extend_from_slice(&(name_utf16.len() as u16).to_le_bytes());
879 body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&name_utf16);
882
883 let _packet = self.build_packet(&mut header, &body);
884
885 Ok(SmbFileId {
887 persistent: 1,
888 volatile: 1,
889 })
890 }
891
892 pub fn read(
894 &mut self,
895 file_id: &SmbFileId,
896 offset: u64,
897 length: u32,
898 ) -> Result<Vec<u8>, SmbError> {
899 if !self.session_active {
900 return Err(SmbError::NotConnected);
901 }
902
903 let read_len = core::cmp::min(length, self.max_read_size);
904
905 let mut header = SmbHeader::new_request(SmbCommand::Read, self.next_message_id());
906 header.session_id = self.session_id;
907 header.tree_id = self.tree_id;
908
909 let mut body = Vec::with_capacity(48);
910 body.extend_from_slice(&49u16.to_le_bytes()); body.push(0); body.push(0); body.extend_from_slice(&read_len.to_le_bytes());
914 body.extend_from_slice(&offset.to_le_bytes());
915 body.extend_from_slice(&file_id.persistent.to_le_bytes());
916 body.extend_from_slice(&file_id.volatile.to_le_bytes());
917 body.extend_from_slice(&1u32.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); let _packet = self.build_packet(&mut header, &body);
924
925 Ok(Vec::new())
927 }
928
929 pub fn write(
931 &mut self,
932 file_id: &SmbFileId,
933 offset: u64,
934 data: &[u8],
935 ) -> Result<u32, SmbError> {
936 if !self.session_active {
937 return Err(SmbError::NotConnected);
938 }
939
940 let write_len = core::cmp::min(data.len(), self.max_write_size as usize);
941
942 let mut header = SmbHeader::new_request(SmbCommand::Write, self.next_message_id());
943 header.session_id = self.session_id;
944 header.tree_id = self.tree_id;
945
946 let data_offset = (SMB2_HEADER_SIZE + 48) as u16;
947 let mut body = Vec::with_capacity(48 + write_len);
948 body.extend_from_slice(&49u16.to_le_bytes()); body.extend_from_slice(&data_offset.to_le_bytes());
950 body.extend_from_slice(&(write_len as u32).to_le_bytes());
951 body.extend_from_slice(&offset.to_le_bytes());
952 body.extend_from_slice(&file_id.persistent.to_le_bytes());
953 body.extend_from_slice(&file_id.volatile.to_le_bytes());
954 body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&data[..write_len]);
960
961 let _packet = self.build_packet(&mut header, &body);
962
963 Ok(write_len as u32)
965 }
966
967 pub fn close(&mut self, file_id: &SmbFileId) -> Result<(), SmbError> {
969 if !self.session_active {
970 return Err(SmbError::NotConnected);
971 }
972
973 let mut header = SmbHeader::new_request(SmbCommand::Close, self.next_message_id());
974 header.session_id = self.session_id;
975 header.tree_id = self.tree_id;
976
977 let mut body = Vec::with_capacity(24);
978 body.extend_from_slice(&24u16.to_le_bytes()); body.extend_from_slice(&0u16.to_le_bytes()); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&file_id.persistent.to_le_bytes());
982 body.extend_from_slice(&file_id.volatile.to_le_bytes());
983
984 let _packet = self.build_packet(&mut header, &body);
985
986 Ok(())
987 }
988
989 pub fn query_directory(
991 &mut self,
992 dir_id: &SmbFileId,
993 pattern: &str,
994 ) -> Result<Vec<SmbDirEntry>, SmbError> {
995 if !self.session_active {
996 return Err(SmbError::NotConnected);
997 }
998
999 let mut header = SmbHeader::new_request(SmbCommand::QueryDirectory, self.next_message_id());
1000 header.session_id = self.session_id;
1001 header.tree_id = self.tree_id;
1002
1003 let pattern_utf16 = NtlmAuth::to_utf16le(pattern);
1004 let pattern_offset = (SMB2_HEADER_SIZE + 32) as u16;
1005
1006 let mut body = Vec::with_capacity(32 + pattern_utf16.len());
1007 body.extend_from_slice(&33u16.to_le_bytes()); body.push(0x25); body.push(0x02); body.extend_from_slice(&0u32.to_le_bytes()); body.extend_from_slice(&dir_id.persistent.to_le_bytes());
1012 body.extend_from_slice(&dir_id.volatile.to_le_bytes());
1013 body.extend_from_slice(&pattern_offset.to_le_bytes());
1014 body.extend_from_slice(&(pattern_utf16.len() as u16).to_le_bytes());
1015 body.extend_from_slice(&65536u32.to_le_bytes()); body.extend_from_slice(&pattern_utf16);
1017
1018 let _packet = self.build_packet(&mut header, &body);
1019
1020 Ok(Vec::new())
1022 }
1023
1024 pub fn sign_message(&self, packet: &mut [u8]) {
1026 if !self.signing_required || packet.len() < SMB2_HEADER_SIZE {
1027 return;
1028 }
1029 for byte in packet.iter_mut().skip(48).take(16) {
1031 *byte = 0;
1032 }
1033
1034 let hmac = self.hmac_sha256(packet);
1036
1037 packet[48..64].copy_from_slice(&hmac[..16]);
1039 }
1040
1041 pub fn verify_signature(&self, packet: &[u8]) -> bool {
1043 if !self.signing_required || packet.len() < SMB2_HEADER_SIZE {
1044 return true;
1045 }
1046
1047 let mut expected_sig = [0u8; 16];
1048 expected_sig.copy_from_slice(&packet[48..64]);
1049
1050 let mut check = packet.to_vec();
1052 for byte in check.iter_mut().skip(48).take(16) {
1053 *byte = 0;
1054 }
1055 let hmac = self.hmac_sha256(&check);
1056
1057 hmac[..16] == expected_sid_to_bytes(&expected_sig)
1058 }
1059
1060 fn hmac_sha256(&self, data: &[u8]) -> [u8; 32] {
1062 let mut result = [0u8; 32];
1064 let mut acc: u32 = 0;
1066 for (i, &b) in self.signing_key.iter().enumerate() {
1067 acc = acc.wrapping_add(b as u32).wrapping_mul(31);
1068 result[i] = b;
1069 }
1070 for (i, &b) in data.iter().enumerate() {
1071 acc = acc.wrapping_add(b as u32).wrapping_mul(37);
1072 result[i % 32] ^= acc as u8;
1073 }
1074 result
1075 }
1076
1077 fn credit_management(&mut self, granted: u16) {
1079 self.credits = self.credits.saturating_sub(1).saturating_add(granted);
1080 }
1081
1082 fn next_message_id(&mut self) -> u64 {
1084 let id = self.message_id;
1085 self.message_id += 1;
1086 id
1087 }
1088
1089 fn build_packet(&mut self, header: &mut SmbHeader, body: &[u8]) -> Vec<u8> {
1091 let hdr_bytes = header.serialize();
1092 let mut packet = Vec::with_capacity(hdr_bytes.len() + body.len());
1093 packet.extend_from_slice(&hdr_bytes);
1094 packet.extend_from_slice(body);
1095
1096 if self.signing_required {
1097 self.sign_message(&mut packet);
1098 }
1099
1100 packet
1101 }
1102
1103 pub fn dialect(&self) -> Option<SmbDialect> {
1105 self.dialect
1106 }
1107
1108 pub fn session_id(&self) -> u64 {
1110 self.session_id
1111 }
1112
1113 pub fn tree_id(&self) -> u32 {
1115 self.tree_id
1116 }
1117
1118 pub fn server_addr(&self) -> &str {
1120 &self.server_addr
1121 }
1122}
1123
1124fn expected_sid_to_bytes(sig: &[u8; 16]) -> [u8; 16] {
1126 *sig
1127}
1128
1129#[cfg(test)]
1134mod tests {
1135 #[allow(unused_imports)]
1136 use alloc::vec;
1137
1138 use super::*;
1139
1140 #[test]
1141 fn test_smb_dialect_from_u16() {
1142 assert_eq!(SmbDialect::from_u16(0x0202), Some(SmbDialect::Smb2_0_2));
1143 assert_eq!(SmbDialect::from_u16(0x0311), Some(SmbDialect::Smb3_1_1));
1144 assert_eq!(SmbDialect::from_u16(0x0000), None);
1145 }
1146
1147 #[test]
1148 fn test_smb_dialect_ordering() {
1149 assert!(SmbDialect::Smb3_1_1 > SmbDialect::Smb2_0_2);
1150 assert!(SmbDialect::Smb3_0 > SmbDialect::Smb2_1);
1151 }
1152
1153 #[test]
1154 fn test_smb_command_from_u16() {
1155 assert_eq!(SmbCommand::from_u16(0), Some(SmbCommand::Negotiate));
1156 assert_eq!(SmbCommand::from_u16(5), Some(SmbCommand::Create));
1157 assert_eq!(SmbCommand::from_u16(99), None);
1158 }
1159
1160 #[test]
1161 fn test_nt_status_from_u32() {
1162 assert_eq!(NtStatus::from_u32(0x0000_0000), NtStatus::Success);
1163 assert_eq!(NtStatus::from_u32(0xC000_0022), NtStatus::AccessDenied);
1164 assert_eq!(NtStatus::from_u32(0xC000_006D), NtStatus::LogonFailure);
1165 }
1166
1167 #[test]
1168 fn test_smb_header_serialize_deserialize() {
1169 let header = SmbHeader::new_request(SmbCommand::Negotiate, 42);
1170 let bytes = header.serialize();
1171 assert_eq!(bytes.len(), SMB2_HEADER_SIZE);
1172
1173 let parsed = SmbHeader::deserialize(&bytes).unwrap();
1174 assert_eq!(parsed.protocol_id, SMB2_MAGIC);
1175 assert_eq!(parsed.command, SmbCommand::Negotiate);
1176 assert_eq!(parsed.message_id, 42);
1177 }
1178
1179 #[test]
1180 fn test_smb_header_bad_magic() {
1181 let mut bytes = SmbHeader::new_request(SmbCommand::Negotiate, 0).serialize();
1182 bytes[0] = 0xFF; assert!(SmbHeader::deserialize(&bytes).is_none());
1184 }
1185
1186 #[test]
1187 fn test_smb_header_too_short() {
1188 assert!(SmbHeader::deserialize(&[0; 10]).is_none());
1189 }
1190
1191 #[test]
1192 fn test_ntlm_negotiate_message() {
1193 let auth = NtlmAuth::new("user", "pass", "DOMAIN");
1194 let msg = auth.negotiate();
1195 assert!(msg.len() >= 32);
1196 assert_eq!(&msg[..8], &NTLMSSP_SIGNATURE);
1197 let msg_type = u32::from_le_bytes([msg[8], msg[9], msg[10], msg[11]]);
1198 assert_eq!(msg_type, NTLM_NEGOTIATE);
1199 }
1200
1201 #[test]
1202 fn test_ntlm_challenge_too_short() {
1203 let mut auth = NtlmAuth::new("user", "pass", "DOMAIN");
1204 assert!(auth.challenge_response(&[0; 10]).is_none());
1205 }
1206
1207 #[test]
1208 fn test_ntlm_challenge_bad_signature() {
1209 let mut auth = NtlmAuth::new("user", "pass", "DOMAIN");
1210 let mut msg = vec![0u8; 40];
1211 msg[..8].copy_from_slice(b"BADMAGIC");
1212 assert!(auth.challenge_response(&msg).is_none());
1213 }
1214
1215 #[test]
1216 fn test_ntlm_nt_hash_deterministic() {
1217 let h1 = NtlmAuth::compute_nt_hash("password");
1218 let h2 = NtlmAuth::compute_nt_hash("password");
1219 assert_eq!(h1, h2);
1220
1221 let h3 = NtlmAuth::compute_nt_hash("different");
1222 assert_ne!(h1, h3);
1223 }
1224
1225 #[test]
1226 fn test_utf16le_conversion() {
1227 let result = NtlmAuth::to_utf16le("AB");
1228 assert_eq!(result, &[0x41, 0x00, 0x42, 0x00]);
1229 }
1230
1231 #[test]
1232 fn test_smb_client_new() {
1233 let client = SmbClient::new("192.168.1.1");
1234 assert_eq!(client.server_addr(), "192.168.1.1");
1235 assert!(client.dialect().is_none());
1236 assert_eq!(client.session_id(), 0);
1237 }
1238
1239 #[test]
1240 fn test_smb_client_tree_connect_not_connected() {
1241 let mut client = SmbClient::new("10.0.0.1");
1242 let result = client.tree_connect("\\\\10.0.0.1\\share");
1243 assert_eq!(result, Err(SmbError::NotConnected));
1244 }
1245}