Skip to main content

smb_server_proto_smb2/
session_setup.rs

1//! SESSION_SETUP ([MS-SMB2] §2.2.5).
2
3
4/// Byte offset of each §2.2.5.1 field within the request body.
5///
6/// Note `Flags` and `SecurityMode` are single-byte fields ([MS-SMB2]
7/// §2.2.5.1). Fixed part totals 24 bytes.
8#[allow(dead_code)]
9mod req_off {
10    /// StructureSize (=25).
11    pub const STRUCT: usize = 0;
12    /// Flags (1 byte).
13    pub const FLAGS: usize = 2;
14    /// SecurityMode (1 byte).
15    pub const SECURITY_MODE: usize = 3;
16    /// Capabilities.
17    pub const CAPABILITIES: usize = 4;
18    /// Channel.
19    pub const CHANNEL: usize = 8;
20    /// SecurityBufferOffset (absolute, from frame start).
21    pub const BLOB_OFFSET: usize = 12;
22    /// SecurityBufferLength.
23    pub const BLOB_LENGTH: usize = 14;
24    /// PrevSessionId.
25    pub const PREV_SESSION_ID: usize = 16;
26    /// Fixed part size before the variable buffer.
27    pub const FIXED_END: usize = 24;
28}
29
30fn g16(b: &[u8], o: usize) -> u16 {
31    match b.get(o..o + 2) {
32        Some(s) => u16::from_le_bytes([s[0], s[1]]),
33        None => 0,
34    }
35}
36
37/// Parsed SESSION_SETUP request (§2.2.5.1).
38#[derive(Debug)]
39pub struct Request {
40    /// Session-setup flags; bit 0 is `SMB2_SESSION_FLAG_BINDING`.
41    pub flags: u8,
42    /// Security mode requested by client.
43    pub security_mode: u16,
44    /// Client capabilities word.
45    pub capabilities: u32,
46    /// Previous session id for reauthentication (usually 0).
47    pub prev_session_id: u64,
48    /// SPNEGO/NTLMSSP token bytes.
49    pub blob: Vec<u8>,
50}
51
52/// `SMB2_SESSION_FLAG_BINDING` — this setup binds a new channel to an existing
53/// session ([MS-SMB2] §2.2.5).
54pub const FLAG_BINDING: u8 = 0x01;
55
56impl Request {
57    /// Parse from the full frame (header included): buffer fields are
58    /// addressed absolutely per §2.2.5.1.
59    pub fn parse(frame: &[u8]) -> Option<Request> {
60        const HDR: usize = 64;
61        let b = frame.get(HDR..)?;
62        if g16(b, req_off::STRUCT) != 25 && g16(b, req_off::STRUCT) != 24 {
63            return None;
64        }
65        let blob_off = g16(b, req_off::BLOB_OFFSET) as usize;
66        let blob_len = g16(b, req_off::BLOB_LENGTH) as usize;
67        // Fall back to "blob follows the fixed part" when the client sent a
68        // zero offset with a non-empty trailing buffer.
69        let (start, end) = if blob_off >= HDR {
70            (blob_off, blob_off.saturating_add(blob_len))
71        } else {
72            (HDR + req_off::FIXED_END, frame.len())
73        };
74        let blob = frame.get(start..end.min(frame.len())).unwrap_or(&[]).to_vec();
75        Some(Request {
76            flags: *b.get(req_off::FLAGS).unwrap_or(&0),
77            security_mode: *b.get(req_off::SECURITY_MODE).unwrap_or(&0) as u16,
78            capabilities: u32::from_le_bytes(
79                b.get(req_off::CAPABILITIES..req_off::CAPABILITIES + 4)
80                    .map(|s| s.try_into().unwrap())
81                    .unwrap_or([0; 4]),
82            ),
83            prev_session_id: u64::from_le_bytes(
84                b.get(req_off::PREV_SESSION_ID..req_off::PREV_SESSION_ID + 8)
85                    .map(|s| s.try_into().unwrap())
86                    .unwrap_or([0; 8]),
87            ),
88            blob,
89        })
90    }
91}
92
93/// Build a SESSION_SETUP response body (§2.2.5.2).
94///
95/// `session_flags`: 0x01 guest, 0x02 null session. The security buffer
96/// directly follows the 8-byte fixed body, i.e. offset 64+8=72 from the
97/// frame start.
98pub fn build_response(session_flags: u16, blob: &[u8]) -> Vec<u8> {
99    let mut b = Vec::with_capacity(8 + blob.len());
100    b.extend_from_slice(&9u16.to_le_bytes()); // StructureSize
101    b.extend_from_slice(&session_flags.to_le_bytes());
102    b.extend_from_slice(&(72u16).to_le_bytes()); // SecurityBufferOffset
103    b.extend_from_slice(&(blob.len() as u16).to_le_bytes());
104    b.extend_from_slice(blob);
105    b
106}
107
108/// Commands (§2.2.1.2).
109pub mod cmd {
110        /// Negotiate command.
111    pub const NEGOTIATE: u16 = 0;
112        /// Session Setup command.
113    pub const SESSION_SETUP: u16 = 1;
114        /// Logoff command.
115    pub const LOGOFF: u16 = 2;
116        /// Tree Connect command.
117    pub const TREE_CONNECT: u16 = 3;
118        /// Tree Disconnect command.
119    pub const TREE_DISCONNECT: u16 = 4;
120        /// Create command.
121    pub const CREATE: u16 = 5;
122        /// Close command.
123    pub const CLOSE: u16 = 6;
124        /// Flush command.
125    pub const FLUSH: u16 = 7;
126        /// Read command.
127    pub const READ: u16 = 8;
128        /// Write command.
129    pub const WRITE: u16 = 9;
130        /// Lock command.
131    pub const LOCK: u16 = 10;
132        /// Ioctl command.
133    pub const IOCTL: u16 = 11;
134        /// Cancel command.
135    pub const CANCEL: u16 = 12;
136        /// Echo command.
137    pub const ECHO: u16 = 13;
138        /// Query Directory command.
139    pub const QUERY_DIRECTORY: u16 = 14;
140        /// Change Notify command.
141    pub const CHANGE_NOTIFY: u16 = 15;
142        /// Query Info command.
143    pub const QUERY_INFO: u16 = 16;
144        /// Set Info command.
145    pub const SET_INFO: u16 = 17;
146        /// Oplock break notification / acknowledgement.
147    pub const OPLOCK_BREAK: u16 = 18;
148}