/src/suricata7/rust/src/smb/smb2.rs
Line | Count | Source |
1 | | /* Copyright (C) 2017-2022 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use nom7::Err; |
19 | | |
20 | | use crate::core::*; |
21 | | |
22 | | use crate::smb::smb::*; |
23 | | use crate::smb::smb2_records::*; |
24 | | use crate::smb::smb2_session::*; |
25 | | use crate::smb::smb2_ioctl::*; |
26 | | use crate::smb::dcerpc::*; |
27 | | use crate::smb::events::*; |
28 | | use crate::smb::files::*; |
29 | | use crate::smb::smb_status::*; |
30 | | |
31 | | pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL: u16 = 0; |
32 | | pub const SMB2_COMMAND_SESSION_SETUP: u16 = 1; |
33 | | pub const SMB2_COMMAND_SESSION_LOGOFF: u16 = 2; |
34 | | pub const SMB2_COMMAND_TREE_CONNECT: u16 = 3; |
35 | | pub const SMB2_COMMAND_TREE_DISCONNECT: u16 = 4; |
36 | | pub const SMB2_COMMAND_CREATE: u16 = 5; |
37 | | pub const SMB2_COMMAND_CLOSE: u16 = 6; |
38 | | pub const SMB2_COMMAND_FLUSH: u16 = 7; |
39 | | pub const SMB2_COMMAND_READ: u16 = 8; |
40 | | pub const SMB2_COMMAND_WRITE: u16 = 9; |
41 | | pub const SMB2_COMMAND_LOCK: u16 = 10; |
42 | | pub const SMB2_COMMAND_IOCTL: u16 = 11; |
43 | | pub const SMB2_COMMAND_CANCEL: u16 = 12; |
44 | | pub const SMB2_COMMAND_KEEPALIVE: u16 = 13; |
45 | | pub const SMB2_COMMAND_FIND: u16 = 14; |
46 | | pub const SMB2_COMMAND_CHANGE_NOTIFY: u16 = 15; |
47 | | pub const SMB2_COMMAND_GET_INFO: u16 = 16; |
48 | | pub const SMB2_COMMAND_SET_INFO: u16 = 17; |
49 | | pub const SMB2_COMMAND_OPLOCK_BREAK: u16 = 18; |
50 | | |
51 | 37.2k | pub fn smb2_command_string(c: u16) -> String { |
52 | 37.2k | match c { |
53 | 4.74k | SMB2_COMMAND_NEGOTIATE_PROTOCOL => "SMB2_COMMAND_NEGOTIATE_PROTOCOL", |
54 | 3.28k | SMB2_COMMAND_SESSION_SETUP => "SMB2_COMMAND_SESSION_SETUP", |
55 | 95 | SMB2_COMMAND_SESSION_LOGOFF => "SMB2_COMMAND_SESSION_LOGOFF", |
56 | 5.36k | SMB2_COMMAND_TREE_CONNECT => "SMB2_COMMAND_TREE_CONNECT", |
57 | 5.80k | SMB2_COMMAND_TREE_DISCONNECT => "SMB2_COMMAND_TREE_DISCONNECT", |
58 | 4.75k | SMB2_COMMAND_CREATE => "SMB2_COMMAND_CREATE", |
59 | 3.61k | SMB2_COMMAND_CLOSE => "SMB2_COMMAND_CLOSE", |
60 | 1.07k | SMB2_COMMAND_READ => "SMB2_COMMAND_READ", |
61 | 29 | SMB2_COMMAND_FLUSH => "SMB2_COMMAND_FLUSH", |
62 | 902 | SMB2_COMMAND_WRITE => "SMB2_COMMAND_WRITE", |
63 | 0 | SMB2_COMMAND_LOCK => "SMB2_COMMAND_LOCK", |
64 | 4.70k | SMB2_COMMAND_IOCTL => "SMB2_COMMAND_IOCTL", |
65 | 0 | SMB2_COMMAND_CANCEL => "SMB2_COMMAND_CANCEL", |
66 | 596 | SMB2_COMMAND_KEEPALIVE => "SMB2_COMMAND_KEEPALIVE", |
67 | 1.18k | SMB2_COMMAND_FIND => "SMB2_COMMAND_FIND", |
68 | 62 | SMB2_COMMAND_CHANGE_NOTIFY => "SMB2_COMMAND_CHANGE_NOTIFY", |
69 | 0 | SMB2_COMMAND_GET_INFO => "SMB2_COMMAND_GET_INFO", |
70 | 41 | SMB2_COMMAND_SET_INFO => "SMB2_COMMAND_SET_INFO", |
71 | 54 | SMB2_COMMAND_OPLOCK_BREAK => "SMB2_COMMAND_OPLOCK_BREAK", |
72 | 931 | _ => { return (c).to_string(); }, |
73 | 36.3k | }.to_string() |
74 | | |
75 | 37.2k | } |
76 | | |
77 | 227k | pub fn smb2_dialect_string(d: u16) -> String { |
78 | 227k | match d { |
79 | 25.3k | 0x0202 => "2.02", |
80 | 5.85k | 0x0210 => "2.10", |
81 | 3.85k | 0x0222 => "2.22", |
82 | 3.85k | 0x0224 => "2.24", |
83 | 1.96k | 0x02ff => "2.??", |
84 | 4.83k | 0x0300 => "3.00", |
85 | 15.1k | 0x0302 => "3.02", |
86 | 4.29k | 0x0310 => "3.10", |
87 | 5.50k | 0x0311 => "3.11", |
88 | 156k | _ => { return (d).to_string(); }, |
89 | 70.6k | }.to_string() |
90 | 227k | } |
91 | | |
92 | | // later we'll use this to determine if we need to |
93 | | // track a ssn per type |
94 | 251k | fn smb2_create_new_tx(cmd: u16) -> bool { |
95 | 251k | match cmd { |
96 | | SMB2_COMMAND_READ | |
97 | | SMB2_COMMAND_WRITE | |
98 | | SMB2_COMMAND_GET_INFO | |
99 | 187k | SMB2_COMMAND_SET_INFO => { false }, |
100 | 64.1k | _ => { true }, |
101 | | } |
102 | 251k | } |
103 | | |
104 | 34.5k | fn smb2_read_response_record_generic(state: &mut SMBState, r: &Smb2Record) |
105 | | { |
106 | 34.5k | if smb2_create_new_tx(r.command) { |
107 | 0 | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
108 | 0 | let tx = state.get_generic_tx(2, r.command, &tx_hdr); |
109 | 0 | if let Some(tx) = tx { |
110 | 0 | tx.set_status(r.nt_status, false); |
111 | 0 | tx.response_done = true; |
112 | 0 | } |
113 | 34.5k | } |
114 | 34.5k | } |
115 | | |
116 | 34.5k | pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32) |
117 | | { |
118 | 34.5k | let max_queue_size = unsafe { SMB_CFG_MAX_READ_QUEUE_SIZE }; |
119 | 34.5k | let max_queue_cnt = unsafe { SMB_CFG_MAX_READ_QUEUE_CNT }; |
120 | | |
121 | 34.5k | smb2_read_response_record_generic(state, r); |
122 | | |
123 | 34.5k | match parse_smb2_response_read(r.data) { |
124 | 29.5k | Ok((_, rd)) => { |
125 | 29.5k | if rd.len - rd.data.len() as u32 > nbss_remaining { |
126 | | // Record claims more bytes than are in NBSS record... |
127 | 2.49k | state.set_event(SMBEvent::ReadResponseTooLarge); |
128 | | // Skip the remaining bytes of the record. |
129 | 2.49k | state.set_skip(Direction::ToClient, nbss_remaining); |
130 | 2.49k | return; |
131 | 27.0k | } |
132 | 27.0k | if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW { |
133 | 7.36k | SCLogDebug!("SMBv2/READ: incomplete record, expecting a follow up"); |
134 | 7.36k | // fall through |
135 | 7.36k | |
136 | 19.6k | } else if r.nt_status != SMB_NTSTATUS_SUCCESS { |
137 | | SCLogDebug!("SMBv2: read response error code received: skip record"); |
138 | 295 | state.set_skip(Direction::ToClient, nbss_remaining); |
139 | 295 | return; |
140 | 19.3k | } |
141 | | |
142 | 26.7k | if (state.max_read_size != 0 && rd.len > state.max_read_size) || |
143 | 26.3k | (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.len }) |
144 | | { |
145 | 399 | state.set_event(SMBEvent::ReadResponseTooLarge); |
146 | 399 | state.set_skip(Direction::ToClient, nbss_remaining); |
147 | 399 | return; |
148 | 26.3k | } |
149 | | |
150 | | SCLogDebug!("SMBv2: read response => {:?}", rd); |
151 | | |
152 | | // get the request info. If we don't have it, there is nothing |
153 | | // we can do except skip this record. |
154 | 26.3k | let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); |
155 | 26.3k | let (offset, file_guid) = match state.ssn2vecoffset_map.remove(&guid_key) { |
156 | 21.8k | Some(o) => (o.offset, o.guid), |
157 | | None => { |
158 | | SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}",rd); |
159 | 4.50k | state.set_skip(Direction::ToClient, nbss_remaining); |
160 | 4.50k | return; |
161 | | }, |
162 | | }; |
163 | | SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset); |
164 | | |
165 | 21.8k | let mut set_event_fileoverlap = false; |
166 | | // look up existing tracker and if we have it update it |
167 | 21.8k | let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient) { |
168 | 2.55k | Some(tx) => { |
169 | 2.55k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
170 | 2.55k | let file_id : u32 = tx.id as u32; |
171 | 2.55k | if offset < tdf.file_tracker.tracked { |
172 | 1.14k | set_event_fileoverlap = true; |
173 | 1.40k | } |
174 | 2.55k | if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { |
175 | 0 | state.set_event(SMBEvent::ReadQueueSizeExceeded); |
176 | 0 | state.set_skip(Direction::ToClient, nbss_remaining); |
177 | 2.55k | } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { |
178 | 0 | state.set_event(SMBEvent::ReadQueueCntExceeded); |
179 | 0 | state.set_skip(Direction::ToClient, nbss_remaining); |
180 | 2.55k | } else { |
181 | 2.55k | filetracker_newchunk(&mut tdf.file_tracker, |
182 | 2.55k | &tdf.file_name, rd.data, offset, |
183 | 2.55k | rd.len, false, &file_id); |
184 | 2.55k | } |
185 | 0 | } |
186 | 2.55k | true |
187 | | }, |
188 | 19.2k | None => { false }, |
189 | | }; |
190 | | SCLogDebug!("existing file tx? {}", found); |
191 | 21.8k | if !found { |
192 | 19.2k | let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); |
193 | 19.2k | let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) { |
194 | 9.82k | Some(n) => (n.name.to_vec(), n.is_pipe), |
195 | 9.45k | _ => { (Vec::new(), false) }, |
196 | | }; |
197 | 19.2k | let mut is_dcerpc = if is_pipe || share_name.is_empty() { |
198 | 18.7k | state.get_service_for_guid(&file_guid).1 |
199 | | } else { |
200 | 515 | false |
201 | | }; |
202 | | SCLogDebug!("SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}", |
203 | | share_name, is_pipe, is_dcerpc); |
204 | | |
205 | 19.2k | if share_name.is_empty() && !is_pipe { |
206 | | SCLogDebug!("SMBv2/READ: no tree connect seen, we don't know if we are a pipe"); |
207 | | |
208 | 9.76k | if smb_dcerpc_probe(rd.data) { |
209 | | SCLogDebug!("SMBv2/READ: looks like dcerpc"); |
210 | | // insert fake tree to assist in follow up lookups |
211 | 6.04k | let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); |
212 | 6.04k | state.ssn2tree_map.insert(tree_key, tree); |
213 | 6.04k | if !is_dcerpc { |
214 | 2.23k | state.guid2name_map.insert(file_guid.to_vec(), b"suricata::dcerpc".to_vec()); |
215 | 3.81k | } |
216 | 6.04k | is_pipe = true; |
217 | 6.04k | is_dcerpc = true; |
218 | 3.71k | } else { |
219 | 3.71k | SCLogDebug!("SMBv2/READ: not DCERPC"); |
220 | 3.71k | } |
221 | 9.51k | } |
222 | | |
223 | 19.2k | if is_pipe && is_dcerpc { |
224 | 13.7k | SCLogDebug!("SMBv2 DCERPC read"); |
225 | 13.7k | let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); |
226 | 13.7k | let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_READ, r.nt_status); |
227 | 13.7k | smb_read_dcerpc_record(state, vercmd, hdr, &file_guid, rd.data); |
228 | 13.7k | } else if is_pipe { |
229 | 1.34k | SCLogDebug!("non-DCERPC pipe"); |
230 | 1.34k | state.set_skip(Direction::ToClient, nbss_remaining); |
231 | 1.34k | } else { |
232 | 4.23k | let file_name = match state.guid2name_map.get(&file_guid) { |
233 | 883 | Some(n) => { n.to_vec() } |
234 | 3.34k | None => { b"<unknown>".to_vec() } |
235 | | }; |
236 | | |
237 | 4.23k | let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToClient); |
238 | 4.23k | tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ); |
239 | 4.23k | tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, |
240 | 4.23k | r.session_id, r.tree_id, 0); // TODO move into new_file_tx |
241 | 4.23k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
242 | 4.23k | tdf.share_name = share_name; |
243 | 4.23k | let file_id : u32 = tx.id as u32; |
244 | 4.23k | if offset < tdf.file_tracker.tracked { |
245 | 0 | set_event_fileoverlap = true; |
246 | 4.23k | } |
247 | 4.23k | if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { |
248 | 0 | state.set_event(SMBEvent::ReadQueueSizeExceeded); |
249 | 0 | state.set_skip(Direction::ToClient, nbss_remaining); |
250 | 4.23k | } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { |
251 | 0 | state.set_event(SMBEvent::ReadQueueCntExceeded); |
252 | 0 | state.set_skip(Direction::ToClient, nbss_remaining); |
253 | 4.23k | } else { |
254 | 4.23k | filetracker_newchunk(&mut tdf.file_tracker, |
255 | 4.23k | &file_name, rd.data, offset, |
256 | 4.23k | rd.len, false, &file_id); |
257 | 4.23k | } |
258 | 0 | } |
259 | | } |
260 | 2.55k | } |
261 | | |
262 | 21.8k | if set_event_fileoverlap { |
263 | 1.14k | state.set_event(SMBEvent::FileOverlap); |
264 | 20.6k | } |
265 | 21.8k | state.set_file_left(Direction::ToClient, rd.len, rd.data.len() as u32, file_guid.to_vec()); |
266 | | } |
267 | 5.04k | _ => { |
268 | 5.04k | SCLogDebug!("SMBv2: failed to parse read response"); |
269 | 5.04k | state.set_event(SMBEvent::MalformedData); |
270 | 5.04k | } |
271 | | } |
272 | 34.5k | } |
273 | | |
274 | 110k | pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32) |
275 | | { |
276 | 110k | let max_queue_size = unsafe { SMB_CFG_MAX_WRITE_QUEUE_SIZE }; |
277 | 110k | let max_queue_cnt = unsafe { SMB_CFG_MAX_WRITE_QUEUE_CNT }; |
278 | | |
279 | | SCLogDebug!("SMBv2/WRITE: request record"); |
280 | 110k | if smb2_create_new_tx(r.command) { |
281 | 0 | let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
282 | 0 | let tx = state.new_generic_tx(2, r.command, tx_key); |
283 | 0 | tx.request_done = true; |
284 | 110k | } |
285 | 110k | match parse_smb2_request_write(r.data) { |
286 | 105k | Ok((_, wr)) => { |
287 | 105k | if wr.wr_len - wr.data.len() as u32 > nbss_remaining { |
288 | | // Record claims more bytes than are in NBSS record... |
289 | 13.7k | state.set_event(SMBEvent::WriteRequestTooLarge); |
290 | | // Skip the remaining bytes of the record. |
291 | 13.7k | state.set_skip(Direction::ToServer, nbss_remaining); |
292 | 13.7k | return; |
293 | 92.0k | } |
294 | 92.0k | if (state.max_write_size != 0 && wr.wr_len > state.max_write_size) || |
295 | 90.0k | (unsafe { SMB_CFG_MAX_WRITE_SIZE != 0 && SMB_CFG_MAX_WRITE_SIZE < wr.wr_len }) { |
296 | 1.95k | state.set_event(SMBEvent::WriteRequestTooLarge); |
297 | 1.95k | state.set_skip(Direction::ToServer, nbss_remaining); |
298 | 1.95k | return; |
299 | 90.0k | } |
300 | | |
301 | | /* update key-guid map */ |
302 | 90.0k | let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GUID); |
303 | 90.0k | state.ssn2vec_map.insert(guid_key, wr.guid.to_vec()); |
304 | | |
305 | 90.0k | let file_guid = wr.guid.to_vec(); |
306 | 90.0k | let file_name = match state.guid2name_map.get(&file_guid) { |
307 | 54.6k | Some(n) => n.to_vec(), |
308 | 35.3k | None => Vec::new(), |
309 | | }; |
310 | | |
311 | 90.0k | let mut set_event_fileoverlap = false; |
312 | 90.0k | let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer) { |
313 | 9.28k | Some(tx) => { |
314 | 9.28k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
315 | 9.28k | let file_id : u32 = tx.id as u32; |
316 | 9.28k | if wr.wr_offset < tdf.file_tracker.tracked { |
317 | 764 | set_event_fileoverlap = true; |
318 | 8.51k | } |
319 | 9.28k | if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() { |
320 | 0 | state.set_event(SMBEvent::WriteQueueSizeExceeded); |
321 | 0 | state.set_skip(Direction::ToServer, nbss_remaining); |
322 | 9.28k | } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { |
323 | 0 | state.set_event(SMBEvent::WriteQueueCntExceeded); |
324 | 0 | state.set_skip(Direction::ToServer, nbss_remaining); |
325 | 9.28k | } else { |
326 | 9.28k | filetracker_newchunk(&mut tdf.file_tracker, |
327 | 9.28k | &file_name, wr.data, wr.wr_offset, |
328 | 9.28k | wr.wr_len, false, &file_id); |
329 | 9.28k | } |
330 | 0 | } |
331 | 9.28k | true |
332 | | }, |
333 | 80.7k | None => { false }, |
334 | | }; |
335 | 90.0k | if !found { |
336 | 80.7k | let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); |
337 | 80.7k | let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) { |
338 | 51.7k | Some(n) => { (n.name.to_vec(), n.is_pipe) }, |
339 | 29.0k | _ => { (Vec::new(), false) }, |
340 | | }; |
341 | 80.7k | let mut is_dcerpc = if is_pipe || share_name.is_empty() { |
342 | 80.3k | state.get_service_for_guid(wr.guid).1 |
343 | | } else { |
344 | 454 | false |
345 | | }; |
346 | | SCLogDebug!("SMBv2/WRITE: share_name {:?} is_pipe {} is_dcerpc {}", |
347 | | share_name, is_pipe, is_dcerpc); |
348 | | |
349 | | // if we missed the TREE connect we can't be sure if 'is_dcerpc' is correct |
350 | 80.7k | if share_name.is_empty() && !is_pipe { |
351 | | SCLogDebug!("SMBv2/WRITE: no tree connect seen, we don't know if we are a pipe"); |
352 | | |
353 | 29.3k | if smb_dcerpc_probe(wr.data) { |
354 | | SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc"); |
355 | | |
356 | 16.2k | let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); |
357 | 16.2k | state.ssn2tree_map.insert(tree_key, tree); |
358 | 16.2k | if !is_dcerpc { |
359 | 8.71k | state.guid2name_map.insert(file_guid.to_vec(), |
360 | 8.71k | b"suricata::dcerpc".to_vec()); |
361 | 8.71k | } |
362 | 16.2k | is_pipe = true; |
363 | 16.2k | is_dcerpc = true; |
364 | 13.0k | } else { |
365 | 13.0k | SCLogDebug!("SMBv2/WRITE: not DCERPC"); |
366 | 13.0k | } |
367 | 51.4k | } |
368 | 80.7k | if is_pipe && is_dcerpc { |
369 | 61.4k | SCLogDebug!("SMBv2 DCERPC write"); |
370 | 61.4k | let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); |
371 | 61.4k | let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_WRITE); |
372 | 61.4k | smb_write_dcerpc_record(state, vercmd, hdr, wr.data); |
373 | 61.4k | } else if is_pipe { |
374 | 5.76k | SCLogDebug!("non-DCERPC pipe: skip rest of the record"); |
375 | 5.76k | state.set_skip(Direction::ToServer, nbss_remaining); |
376 | 5.76k | } else { |
377 | 13.5k | let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToServer); |
378 | 13.5k | tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE); |
379 | 13.5k | tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, |
380 | 13.5k | r.session_id, r.tree_id, 0); // TODO move into new_file_tx |
381 | 13.5k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
382 | 13.5k | let file_id : u32 = tx.id as u32; |
383 | 13.5k | if wr.wr_offset < tdf.file_tracker.tracked { |
384 | 0 | set_event_fileoverlap = true; |
385 | 13.5k | } |
386 | | |
387 | 13.5k | if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() { |
388 | 0 | state.set_event(SMBEvent::WriteQueueSizeExceeded); |
389 | 0 | state.set_skip(Direction::ToServer, nbss_remaining); |
390 | 13.5k | } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { |
391 | 0 | state.set_event(SMBEvent::WriteQueueCntExceeded); |
392 | 0 | state.set_skip(Direction::ToServer, nbss_remaining); |
393 | 13.5k | } else { |
394 | 13.5k | filetracker_newchunk(&mut tdf.file_tracker, |
395 | 13.5k | &file_name, wr.data, wr.wr_offset, |
396 | 13.5k | wr.wr_len, false, &file_id); |
397 | 13.5k | } |
398 | 0 | } |
399 | | } |
400 | 9.28k | } |
401 | | |
402 | 90.0k | if set_event_fileoverlap { |
403 | 764 | state.set_event(SMBEvent::FileOverlap); |
404 | 89.3k | } |
405 | 90.0k | state.set_file_left(Direction::ToServer, wr.wr_len, wr.data.len() as u32, file_guid.to_vec()); |
406 | | }, |
407 | 4.79k | _ => { |
408 | 4.79k | state.set_event(SMBEvent::MalformedData); |
409 | 4.79k | }, |
410 | | } |
411 | 110k | } |
412 | | |
413 | 375k | pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) |
414 | | { |
415 | | SCLogDebug!("SMBv2 request record, command {} tree {} session {}", |
416 | | &smb2_command_string(r.command), r.tree_id, r.session_id); |
417 | | |
418 | 375k | let mut events : Vec<SMBEvent> = Vec::new(); |
419 | | |
420 | 375k | let have_tx = match r.command { |
421 | | SMB2_COMMAND_SET_INFO => { |
422 | | SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", r); |
423 | 24.6k | let have_si_tx = match parse_smb2_request_setinfo(r.data) { |
424 | 13.1k | Ok((_, rd)) => { |
425 | | SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", rd); |
426 | | |
427 | 13.1k | match rd.data { |
428 | 1.59k | Smb2SetInfoRequestData::RENAME(ref ren) => { |
429 | 1.59k | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
430 | 1.59k | let mut newname = ren.name.to_vec(); |
431 | 16.4k | newname.retain(|&i|i != 0x00); |
432 | 1.59k | let oldname = match state.guid2name_map.get(rd.guid) { |
433 | 213 | Some(n) => { n.to_vec() }, |
434 | 1.37k | None => { b"<unknown>".to_vec() }, |
435 | | }; |
436 | 1.59k | let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname); |
437 | 1.59k | tx.hdr = tx_hdr; |
438 | 1.59k | tx.request_done = true; |
439 | 1.59k | tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO); |
440 | 1.59k | true |
441 | | } |
442 | 10.3k | Smb2SetInfoRequestData::DISPOSITION(ref dis) => { |
443 | 10.3k | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
444 | 10.3k | let fname = match state.guid2name_map.get(rd.guid) { |
445 | 226 | Some(n) => { n.to_vec() }, |
446 | | None => { |
447 | | // try to find latest created file in case of chained commands |
448 | 10.1k | let mut guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); |
449 | 10.1k | if guid_key.msg_id == 0 { |
450 | 194 | b"<unknown>".to_vec() |
451 | | } else { |
452 | 9.92k | guid_key.msg_id -= 1; |
453 | 9.92k | match state.ssn2vec_map.get(&guid_key) { |
454 | 47 | Some(n) => { n.to_vec() }, |
455 | 9.87k | None => { b"<unknown>".to_vec()}, |
456 | | } |
457 | | } |
458 | | }, |
459 | | }; |
460 | 10.3k | let tx = state.new_setfileinfo_tx(fname, rd.guid.to_vec(), rd.class as u16, rd.infolvl as u16, dis.delete); |
461 | 10.3k | tx.hdr = tx_hdr; |
462 | 10.3k | tx.request_done = true; |
463 | 10.3k | tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO); |
464 | 10.3k | true |
465 | | } |
466 | 1.19k | _ => false, |
467 | | } |
468 | | }, |
469 | 11.4k | Err(Err::Incomplete(_n)) => { |
470 | | SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _n); |
471 | 11.4k | events.push(SMBEvent::MalformedData); |
472 | 11.4k | false |
473 | | }, |
474 | 0 | Err(Err::Error(_e)) | |
475 | 0 | Err(Err::Failure(_e)) => { |
476 | | SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _e); |
477 | 0 | events.push(SMBEvent::MalformedData); |
478 | 0 | false |
479 | | }, |
480 | | }; |
481 | 24.6k | have_si_tx |
482 | | }, |
483 | | SMB2_COMMAND_IOCTL => { |
484 | 21.2k | smb2_ioctl_request_record(state, r); |
485 | 21.2k | true |
486 | | }, |
487 | | SMB2_COMMAND_TREE_DISCONNECT => { |
488 | 15.6k | let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); |
489 | 15.6k | state.ssn2tree_map.remove(&tree_key); |
490 | 15.6k | false |
491 | | } |
492 | | SMB2_COMMAND_NEGOTIATE_PROTOCOL => { |
493 | 27.4k | match parse_smb2_request_negotiate_protocol(r.data) { |
494 | 13.1k | Ok((_, rd)) => { |
495 | 13.1k | let mut dialects : Vec<Vec<u8>> = Vec::new(); |
496 | 225k | for d in rd.dialects_vec { |
497 | 212k | SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d)); |
498 | 212k | let dvec = smb2_dialect_string(d).as_bytes().to_vec(); |
499 | 212k | dialects.push(dvec); |
500 | 212k | } |
501 | | |
502 | 13.1k | let found = match state.get_negotiate_tx(2) { |
503 | | Some(_) => { |
504 | | SCLogDebug!("WEIRD, should not have NEGOTIATE tx!"); |
505 | 4.09k | true |
506 | | }, |
507 | 9.09k | None => { false }, |
508 | | }; |
509 | 13.1k | if !found { |
510 | 9.09k | let tx = state.new_negotiate_tx(2); |
511 | 9.09k | if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { |
512 | 9.09k | tdn.dialects2 = dialects; |
513 | 9.09k | tdn.client_guid = Some(rd.client_guid.to_vec()); |
514 | 9.09k | } |
515 | 9.09k | tx.request_done = true; |
516 | 4.09k | } |
517 | 13.1k | true |
518 | | }, |
519 | | _ => { |
520 | 14.2k | events.push(SMBEvent::MalformedData); |
521 | 14.2k | false |
522 | | }, |
523 | | } |
524 | | }, |
525 | | SMB2_COMMAND_SESSION_SETUP => { |
526 | 79.2k | smb2_session_setup_request(state, r); |
527 | 79.2k | true |
528 | | }, |
529 | | SMB2_COMMAND_TREE_CONNECT => { |
530 | 18.8k | match parse_smb2_request_tree_connect(r.data) { |
531 | 15.7k | Ok((_, tr)) => { |
532 | 15.7k | let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); |
533 | 15.7k | let mut name_val = tr.share_name.to_vec(); |
534 | 10.3M | name_val.retain(|&i|i != 0x00); |
535 | 15.7k | if name_val.len() > 1 { |
536 | 14.5k | name_val = name_val[1..].to_vec(); |
537 | 14.5k | } |
538 | | |
539 | 15.7k | let tx = state.new_treeconnect_tx(name_key, name_val); |
540 | 15.7k | tx.request_done = true; |
541 | 15.7k | tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT); |
542 | 15.7k | true |
543 | | } |
544 | | _ => { |
545 | 3.15k | events.push(SMBEvent::MalformedData); |
546 | 3.15k | false |
547 | | }, |
548 | | } |
549 | | }, |
550 | | SMB2_COMMAND_READ => { |
551 | 24.9k | match parse_smb2_request_read(r.data) { |
552 | 17.8k | Ok((_, rd)) => { |
553 | 17.8k | if (state.max_read_size != 0 && rd.rd_len > state.max_read_size) || |
554 | 17.0k | (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len }) { |
555 | 2.95k | events.push(SMBEvent::ReadRequestTooLarge); |
556 | 14.9k | } else { |
557 | 14.9k | SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}", |
558 | 14.9k | rd.guid, rd.rd_len, rd.rd_offset); |
559 | 14.9k | |
560 | 14.9k | // store read guid,offset in map |
561 | 14.9k | let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); |
562 | 14.9k | let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset); |
563 | 14.9k | state.ssn2vecoffset_map.insert(guid_key, guidoff); |
564 | 14.9k | } |
565 | | }, |
566 | 7.05k | _ => { |
567 | 7.05k | events.push(SMBEvent::MalformedData); |
568 | 7.05k | }, |
569 | | } |
570 | 24.9k | false |
571 | | }, |
572 | | SMB2_COMMAND_CREATE => { |
573 | 41.5k | match parse_smb2_request_create(r.data) { |
574 | 28.2k | Ok((_, cr)) => { |
575 | 28.2k | let del = cr.create_options & 0x0000_1000 != 0; |
576 | 28.2k | let dir = cr.create_options & 0x0000_0001 != 0; |
577 | | |
578 | | SCLogDebug!("create_options {:08x}", cr.create_options); |
579 | | |
580 | 28.2k | let name_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); |
581 | 28.2k | state.ssn2vec_map.insert(name_key, cr.data.to_vec()); |
582 | | |
583 | 28.2k | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
584 | 28.2k | let tx = state.new_create_tx(cr.data, |
585 | 28.2k | cr.disposition, del, dir, tx_hdr); |
586 | 28.2k | tx.vercmd.set_smb2_cmd(r.command); |
587 | | SCLogDebug!("TS CREATE TX {} created", tx.id); |
588 | 28.2k | true |
589 | | }, |
590 | | _ => { |
591 | 13.3k | events.push(SMBEvent::MalformedData); |
592 | 13.3k | false |
593 | | }, |
594 | | } |
595 | | }, |
596 | | SMB2_COMMAND_WRITE => { |
597 | 99.1k | smb2_write_request_record(state, r, 0); |
598 | 99.1k | true // write handling creates both file tx and generic tx |
599 | | }, |
600 | | SMB2_COMMAND_CLOSE => { |
601 | 7.85k | match parse_smb2_request_close(r.data) { |
602 | 6.98k | Ok((_, cd)) => { |
603 | 6.98k | let found_ts = match state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) { |
604 | 1.83k | Some(tx) => { |
605 | 1.83k | if !tx.request_done { |
606 | 1.09k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
607 | 1.09k | filetracker_close(&mut tdf.file_tracker); |
608 | 1.09k | } |
609 | 733 | } |
610 | 1.83k | tx.request_done = true; |
611 | 1.83k | tx.response_done = true; |
612 | 1.83k | tx.set_status(SMB_NTSTATUS_SUCCESS, false); |
613 | 1.83k | true |
614 | | }, |
615 | 5.14k | None => { false }, |
616 | | }; |
617 | 6.98k | let found_tc = match state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) { |
618 | 1.24k | Some(tx) => { |
619 | 1.24k | if !tx.request_done { |
620 | 616 | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
621 | 616 | filetracker_close(&mut tdf.file_tracker); |
622 | 616 | } |
623 | 633 | } |
624 | 1.24k | tx.request_done = true; |
625 | 1.24k | tx.response_done = true; |
626 | 1.24k | tx.set_status(SMB_NTSTATUS_SUCCESS, false); |
627 | 1.24k | true |
628 | | }, |
629 | 5.73k | None => { false }, |
630 | | }; |
631 | 6.98k | if !found_ts && !found_tc { |
632 | 4.54k | SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid); |
633 | 4.54k | } |
634 | | }, |
635 | 875 | _ => { |
636 | 875 | events.push(SMBEvent::MalformedData); |
637 | 875 | }, |
638 | | } |
639 | 7.85k | false |
640 | | }, |
641 | | _ => { |
642 | 14.4k | false |
643 | | }, |
644 | | }; |
645 | | /* if we don't have a tx, create it here (maybe) */ |
646 | 375k | if !have_tx && smb2_create_new_tx(r.command) { |
647 | 64.1k | let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
648 | 64.1k | let tx = state.new_generic_tx(2, r.command, tx_key); |
649 | 64.1k | SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}", |
650 | 64.1k | tx.id, r.command, r.session_id, r.tree_id, r.message_id); |
651 | 64.1k | tx.set_events(events); |
652 | 310k | } |
653 | 375k | } |
654 | | |
655 | 178k | pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) |
656 | | { |
657 | | SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}", |
658 | | &smb2_command_string(r.command), r.nt_status, |
659 | | r.tree_id, r.session_id, r.message_id); |
660 | | |
661 | 178k | let mut events : Vec<SMBEvent> = Vec::new(); |
662 | | |
663 | 178k | let have_tx = match r.command { |
664 | | SMB2_COMMAND_IOCTL => { |
665 | 30.1k | smb2_ioctl_response_record(state, r); |
666 | 30.1k | true |
667 | | }, |
668 | | SMB2_COMMAND_SESSION_SETUP => { |
669 | 15.9k | smb2_session_setup_response(state, r); |
670 | 15.9k | true |
671 | | }, |
672 | | SMB2_COMMAND_WRITE => { |
673 | 4.03k | if r.nt_status == SMB_NTSTATUS_SUCCESS { |
674 | 3.45k | match parse_smb2_response_write(r.data) |
675 | | { |
676 | 2.02k | Ok((_, _wr)) => { |
677 | 2.02k | SCLogDebug!("SMBv2: Write response => {:?}", _wr); |
678 | 2.02k | |
679 | 2.02k | /* search key-guid map */ |
680 | 2.02k | let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID, |
681 | 2.02k | r.session_id, r.tree_id, r.message_id); |
682 | 2.02k | let _guid_vec = state.ssn2vec_map.remove(&guid_key).unwrap_or_default(); |
683 | 2.02k | SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec); |
684 | 2.02k | } |
685 | 1.43k | _ => { |
686 | 1.43k | events.push(SMBEvent::MalformedData); |
687 | 1.43k | }, |
688 | | } |
689 | 571 | } |
690 | 4.03k | false // the request may have created a generic tx, so handle that here |
691 | | }, |
692 | | SMB2_COMMAND_READ => { |
693 | 39.0k | if r.nt_status == SMB_NTSTATUS_SUCCESS || |
694 | 15.1k | r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW { |
695 | 27.4k | smb2_read_response_record(state, r, 0); |
696 | 27.4k | false |
697 | | |
698 | 11.5k | } else if r.nt_status == SMB_NTSTATUS_END_OF_FILE { |
699 | | SCLogDebug!("SMBv2: read response => EOF"); |
700 | | |
701 | 7.14k | let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); |
702 | 7.14k | let file_guid = match state.ssn2vecoffset_map.remove(&guid_key) { |
703 | 4.72k | Some(o) => o.guid, |
704 | | _ => { |
705 | | SCLogDebug!("SMBv2 READ response: reply to unknown request"); |
706 | 2.42k | Vec::new() |
707 | | }, |
708 | | }; |
709 | 7.14k | let found = match state.get_file_tx_by_fuid(&file_guid, Direction::ToClient) { |
710 | 3.71k | Some(tx) => { |
711 | 3.71k | if !tx.request_done { |
712 | 3.08k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
713 | 3.08k | filetracker_close(&mut tdf.file_tracker); |
714 | 3.08k | } |
715 | 624 | } |
716 | 3.71k | tx.set_status(r.nt_status, false); |
717 | 3.71k | tx.request_done = true; |
718 | 3.71k | false |
719 | | }, |
720 | 3.43k | None => { false }, |
721 | | }; |
722 | 7.14k | if !found { |
723 | 7.14k | SCLogDebug!("SMBv2 READ: no TX at GUID {:?}", file_guid); |
724 | 7.14k | } |
725 | 7.14k | false |
726 | | } else { |
727 | | SCLogDebug!("SMBv2 READ: status {}", r.nt_status); |
728 | 4.39k | false |
729 | | } |
730 | | }, |
731 | | SMB2_COMMAND_CREATE => { |
732 | 34.0k | if r.nt_status == SMB_NTSTATUS_SUCCESS { |
733 | 32.4k | match parse_smb2_response_create(r.data) { |
734 | 27.0k | Ok((_, cr)) => { |
735 | | SCLogDebug!("SMBv2: Create response => {:?}", cr); |
736 | | |
737 | 27.0k | let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); |
738 | 27.0k | if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { |
739 | 431k | p.retain(|&i|i != 0x00); |
740 | 15.2k | state.guid2name_map.insert(cr.guid.to_vec(), p); |
741 | 11.7k | } else { |
742 | 11.7k | SCLogDebug!("SMBv2 response: GUID NOT FOUND"); |
743 | 11.7k | } |
744 | | |
745 | 27.0k | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
746 | 27.0k | if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) { |
747 | | SCLogDebug!("tx {} with {}/{} marked as done", |
748 | | tx.id, r.command, &smb2_command_string(r.command)); |
749 | 8.34k | tx.set_status(r.nt_status, false); |
750 | 8.34k | tx.response_done = true; |
751 | | |
752 | 7.36k | if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data { |
753 | 7.36k | tdn.create_ts = cr.create_ts.as_unix(); |
754 | 7.36k | tdn.last_access_ts = cr.last_access_ts.as_unix(); |
755 | 7.36k | tdn.last_write_ts = cr.last_write_ts.as_unix(); |
756 | 7.36k | tdn.last_change_ts = cr.last_change_ts.as_unix(); |
757 | 7.36k | tdn.size = cr.size; |
758 | 7.36k | tdn.guid = cr.guid.to_vec(); |
759 | 7.36k | } |
760 | 18.7k | } |
761 | | } |
762 | 5.42k | _ => { |
763 | 5.42k | events.push(SMBEvent::MalformedData); |
764 | 5.42k | }, |
765 | | } |
766 | 32.4k | true |
767 | | } else { |
768 | 1.58k | false |
769 | | } |
770 | | }, |
771 | | SMB2_COMMAND_TREE_DISCONNECT => { |
772 | | // normally removed when processing request, |
773 | | // but in case we missed that try again here |
774 | 7.19k | let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); |
775 | 7.19k | state.ssn2tree_map.remove(&tree_key); |
776 | 7.19k | false |
777 | | } |
778 | | SMB2_COMMAND_TREE_CONNECT => { |
779 | 16.5k | if r.nt_status == SMB_NTSTATUS_SUCCESS { |
780 | 10.6k | match parse_smb2_response_tree_connect(r.data) { |
781 | 8.36k | Ok((_, tr)) => { |
782 | 8.36k | let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); |
783 | 8.36k | let mut share_name = Vec::new(); |
784 | 8.36k | let is_pipe = tr.share_type == 2; |
785 | 8.36k | let found = match state.get_treeconnect_tx(name_key) { |
786 | 2.45k | Some(tx) => { |
787 | 2.45k | if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { |
788 | 2.45k | tdn.share_type = tr.share_type; |
789 | 2.45k | tdn.is_pipe = is_pipe; |
790 | 2.45k | tdn.tree_id = r.tree_id; |
791 | 2.45k | share_name = tdn.share_name.to_vec(); |
792 | 2.45k | } |
793 | | // update hdr now that we have a tree_id |
794 | 2.45k | tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); |
795 | 2.45k | tx.response_done = true; |
796 | 2.45k | tx.set_status(r.nt_status, false); |
797 | 2.45k | true |
798 | | }, |
799 | 5.91k | None => { false }, |
800 | | }; |
801 | 8.36k | if found { |
802 | 2.45k | let tree = SMBTree::new(share_name.to_vec(), is_pipe); |
803 | 2.45k | let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); |
804 | 2.45k | state.ssn2tree_map.insert(tree_key, tree); |
805 | 5.91k | } |
806 | 8.36k | true |
807 | | } |
808 | | _ => { |
809 | 2.24k | events.push(SMBEvent::MalformedData); |
810 | 2.24k | false |
811 | | }, |
812 | | } |
813 | | } else { |
814 | 5.97k | let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); |
815 | 5.97k | let found = match state.get_treeconnect_tx(name_key) { |
816 | 966 | Some(tx) => { |
817 | 966 | tx.response_done = true; |
818 | 966 | tx.set_status(r.nt_status, false); |
819 | 966 | true |
820 | | }, |
821 | 5.01k | None => { false }, |
822 | | }; |
823 | 5.97k | found |
824 | | } |
825 | | }, |
826 | | SMB2_COMMAND_NEGOTIATE_PROTOCOL => { |
827 | 18.6k | let res = if r.nt_status == SMB_NTSTATUS_SUCCESS { |
828 | 11.7k | parse_smb2_response_negotiate_protocol(r.data) |
829 | | } else { |
830 | 6.97k | parse_smb2_response_negotiate_protocol_error(r.data) |
831 | | }; |
832 | 18.6k | match res { |
833 | 10.6k | Ok((_, rd)) => { |
834 | | SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect)); |
835 | | |
836 | 10.6k | let smb_cfg_max_read_size = unsafe { SMB_CFG_MAX_READ_SIZE }; |
837 | 10.6k | if smb_cfg_max_read_size != 0 && rd.max_read_size > smb_cfg_max_read_size { |
838 | 3.81k | state.set_event(SMBEvent::NegotiateMaxReadSizeTooLarge); |
839 | 6.78k | } |
840 | 10.6k | let smb_cfg_max_write_size = unsafe { SMB_CFG_MAX_WRITE_SIZE }; |
841 | 10.6k | if smb_cfg_max_write_size != 0 && rd.max_write_size > smb_cfg_max_write_size { |
842 | 3.82k | state.set_event(SMBEvent::NegotiateMaxWriteSizeTooLarge); |
843 | 6.78k | } |
844 | | |
845 | 10.6k | state.dialect = rd.dialect; |
846 | 10.6k | state.max_read_size = rd.max_read_size; |
847 | 10.6k | state.max_write_size = rd.max_write_size; |
848 | | |
849 | 10.6k | let found2 = match state.get_negotiate_tx(2) { |
850 | 1.04k | Some(tx) => { |
851 | 1.04k | if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { |
852 | 1.04k | tdn.server_guid = rd.server_guid.to_vec(); |
853 | 1.04k | } |
854 | 1.04k | tx.set_status(r.nt_status, false); |
855 | 1.04k | tx.response_done = true; |
856 | 1.04k | true |
857 | | }, |
858 | 9.56k | None => { false }, |
859 | | }; |
860 | | // SMB2 response to SMB1 request? |
861 | 10.6k | let found1 = !found2 && match state.get_negotiate_tx(1) { |
862 | 862 | Some(tx) => { |
863 | 862 | if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { |
864 | 862 | tdn.server_guid = rd.server_guid.to_vec(); |
865 | 862 | } |
866 | 862 | tx.set_status(r.nt_status, false); |
867 | 862 | tx.response_done = true; |
868 | 862 | true |
869 | | }, |
870 | 8.70k | None => { false }, |
871 | | }; |
872 | 10.6k | found1 || found2 |
873 | | }, |
874 | | _ => { |
875 | 8.09k | events.push(SMBEvent::MalformedData); |
876 | 8.09k | false |
877 | | } |
878 | | } |
879 | | }, |
880 | | _ => { |
881 | | SCLogDebug!("default case: no TX"); |
882 | 12.3k | false |
883 | | }, |
884 | | }; |
885 | 178k | if !have_tx { |
886 | 88.2k | let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); |
887 | | SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}", |
888 | | &smb2_command_string(r.command), |
889 | | r.session_id, r.tree_id, r.message_id); |
890 | 88.2k | let _found = match state.get_generic_tx(2, r.command, &tx_hdr) { |
891 | 7.16k | Some(tx) => { |
892 | | SCLogDebug!("tx {} with {}/{} marked as done", |
893 | | tx.id, r.command, &smb2_command_string(r.command)); |
894 | 7.16k | if r.nt_status != SMB_NTSTATUS_PENDING { |
895 | 6.97k | tx.response_done = true; |
896 | 6.97k | } |
897 | 7.16k | tx.set_status(r.nt_status, false); |
898 | 7.16k | tx.set_events(events); |
899 | 7.16k | true |
900 | | }, |
901 | | _ => { |
902 | | SCLogDebug!("no tx found for {:?}", r); |
903 | 81.0k | false |
904 | | }, |
905 | | }; |
906 | 89.8k | } |
907 | 178k | } |