/src/suricata7/rust/src/smb/smb1.rs
Line | Count | Source |
1 | | /* Copyright (C) 2018-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 | | /* TODO |
19 | | * - check all parsers for calls on non-SUCCESS status |
20 | | */ |
21 | | |
22 | | use crate::core::*; |
23 | | |
24 | | use crate::smb::smb::*; |
25 | | use crate::smb::dcerpc::*; |
26 | | use crate::smb::events::*; |
27 | | use crate::smb::files::*; |
28 | | |
29 | | use crate::smb::smb1_records::*; |
30 | | use crate::smb::smb1_session::*; |
31 | | |
32 | | use crate::smb::smb_status::*; |
33 | | |
34 | | use nom7::Err; |
35 | | |
36 | | // https://msdn.microsoft.com/en-us/library/ee441741.aspx |
37 | | pub const SMB1_COMMAND_CREATE_DIRECTORY: u8 = 0x00; |
38 | | pub const SMB1_COMMAND_DELETE_DIRECTORY: u8 = 0x01; |
39 | | pub const SMB1_COMMAND_OPEN: u8 = 0x02; |
40 | | pub const SMB1_COMMAND_CREATE: u8 = 0x03; |
41 | | pub const SMB1_COMMAND_CLOSE: u8 = 0x04; |
42 | | pub const SMB1_COMMAND_FLUSH: u8 = 0x05; |
43 | | pub const SMB1_COMMAND_DELETE: u8 = 0x06; |
44 | | pub const SMB1_COMMAND_RENAME: u8 = 0x07; |
45 | | pub const SMB1_COMMAND_QUERY_INFORMATION: u8 = 0x08; |
46 | | pub const SMB1_COMMAND_SET_INFORMATION: u8 = 0x09; |
47 | | pub const SMB1_COMMAND_READ: u8 = 0x0a; |
48 | | pub const SMB1_COMMAND_WRITE: u8 = 0x0b; |
49 | | pub const SMB1_COMMAND_LOCK_BYTE_RANGE: u8 = 0x0c; |
50 | | pub const SMB1_COMMAND_UNLOCK_BYTE_RANGE: u8 = 0x0d; |
51 | | pub const SMB1_COMMAND_CREATE_TEMPORARY: u8 = 0x0e; |
52 | | pub const SMB1_COMMAND_CREATE_NEW: u8 = 0x0f; |
53 | | pub const SMB1_COMMAND_CHECK_DIRECTORY: u8 = 0x10; |
54 | | pub const SMB1_COMMAND_PROCESS_EXIT: u8 = 0x11; |
55 | | pub const SMB1_COMMAND_SEEK: u8 = 0x12; |
56 | | pub const SMB1_COMMAND_LOCK_AND_READ: u8 = 0x13; |
57 | | pub const SMB1_COMMAND_WRITE_AND_UNLOCK: u8 = 0x14; |
58 | | pub const SMB1_COMMAND_LOCKING_ANDX: u8 = 0x24; |
59 | | pub const SMB1_COMMAND_TRANS: u8 = 0x25; |
60 | | pub const SMB1_COMMAND_ECHO: u8 = 0x2b; |
61 | | pub const SMB1_COMMAND_WRITE_AND_CLOSE: u8 = 0x2c; |
62 | | pub const SMB1_COMMAND_OPEN_ANDX: u8 = 0x2d; |
63 | | pub const SMB1_COMMAND_READ_ANDX: u8 = 0x2e; |
64 | | pub const SMB1_COMMAND_WRITE_ANDX: u8 = 0x2f; |
65 | | pub const SMB1_COMMAND_TRANS2: u8 = 0x32; |
66 | | pub const SMB1_COMMAND_TRANS2_SECONDARY: u8 = 0x33; |
67 | | pub const SMB1_COMMAND_FIND_CLOSE2: u8 = 0x34; |
68 | | pub const SMB1_COMMAND_TREE_DISCONNECT: u8 = 0x71; |
69 | | pub const SMB1_COMMAND_NEGOTIATE_PROTOCOL: u8 = 0x72; |
70 | | pub const SMB1_COMMAND_SESSION_SETUP_ANDX: u8 = 0x73; |
71 | | pub const SMB1_COMMAND_LOGOFF_ANDX: u8 = 0x74; |
72 | | pub const SMB1_COMMAND_TREE_CONNECT_ANDX: u8 = 0x75; |
73 | | pub const SMB1_COMMAND_QUERY_INFO_DISK: u8 = 0x80; |
74 | | pub const SMB1_COMMAND_NT_TRANS: u8 = 0xa0; |
75 | | pub const SMB1_COMMAND_NT_TRANS_SECONDARY: u8 = 0xa1; |
76 | | pub const SMB1_COMMAND_NT_CREATE_ANDX: u8 = 0xa2; |
77 | | pub const SMB1_COMMAND_NT_CANCEL: u8 = 0xa4; |
78 | | pub const SMB1_COMMAND_NONE: u8 = 0xff; |
79 | | |
80 | 62.0k | pub fn smb1_command_string(c: u8) -> String { |
81 | 62.0k | match c { |
82 | 6 | SMB1_COMMAND_CREATE_DIRECTORY => "SMB1_COMMAND_CREATE_DIRECTORY", |
83 | 1 | SMB1_COMMAND_DELETE_DIRECTORY => "SMB1_COMMAND_DELETE_DIRECTORY", |
84 | 1 | SMB1_COMMAND_OPEN => "SMB1_COMMAND_OPEN", |
85 | 1 | SMB1_COMMAND_CREATE => "SMB1_COMMAND_CREATE", |
86 | 3.23k | SMB1_COMMAND_CLOSE => "SMB1_COMMAND_CLOSE", |
87 | 0 | SMB1_COMMAND_FLUSH => "SMB1_COMMAND_FLUSH", |
88 | 1 | SMB1_COMMAND_DELETE => "SMB1_COMMAND_DELETE", |
89 | 49 | SMB1_COMMAND_RENAME => "SMB1_COMMAND_RENAME", |
90 | 1 | SMB1_COMMAND_QUERY_INFORMATION => "SMB1_COMMAND_QUERY_INFORMATION", |
91 | 0 | SMB1_COMMAND_SET_INFORMATION => "SMB1_COMMAND_SET_INFORMATION", |
92 | 0 | SMB1_COMMAND_READ => "SMB1_COMMAND_READ", |
93 | 7 | SMB1_COMMAND_WRITE => "SMB1_COMMAND_WRITE", |
94 | 0 | SMB1_COMMAND_LOCK_BYTE_RANGE => "SMB1_COMMAND_LOCK_BYTE_RANGE", |
95 | 0 | SMB1_COMMAND_UNLOCK_BYTE_RANGE => "SMB1_COMMAND_UNLOCK_BYTE_RANGE", |
96 | 0 | SMB1_COMMAND_CREATE_TEMPORARY => "SMB1_COMMAND_CREATE_TEMPORARY", |
97 | 1 | SMB1_COMMAND_CREATE_NEW => "SMB1_COMMAND_CREATE_NEW", |
98 | 0 | SMB1_COMMAND_CHECK_DIRECTORY => "SMB1_COMMAND_CHECK_DIRECTORY", |
99 | 1 | SMB1_COMMAND_PROCESS_EXIT => "SMB1_COMMAND_PROCESS_EXIT", |
100 | 0 | SMB1_COMMAND_SEEK => "SMB1_COMMAND_SEEK", |
101 | 0 | SMB1_COMMAND_LOCK_AND_READ => "SMB1_COMMAND_LOCK_AND_READ", |
102 | 1 | SMB1_COMMAND_WRITE_AND_UNLOCK => "SMB1_COMMAND_WRITE_AND_UNLOCK", |
103 | 5.12k | SMB1_COMMAND_LOCKING_ANDX => "SMB1_COMMAND_LOCKING_ANDX", |
104 | 35 | SMB1_COMMAND_ECHO => "SMB1_COMMAND_ECHO", |
105 | 3 | SMB1_COMMAND_WRITE_AND_CLOSE => "SMB1_COMMAND_WRITE_AND_CLOSE", |
106 | 0 | SMB1_COMMAND_OPEN_ANDX => "SMB1_COMMAND_OPEN_ANDX", |
107 | 5.30k | SMB1_COMMAND_READ_ANDX => "SMB1_COMMAND_READ_ANDX", |
108 | 266 | SMB1_COMMAND_WRITE_ANDX => "SMB1_COMMAND_WRITE_ANDX", |
109 | 2.34k | SMB1_COMMAND_TRANS => "SMB1_COMMAND_TRANS", |
110 | 0 | SMB1_COMMAND_TRANS2 => "SMB1_COMMAND_TRANS2", |
111 | 89 | SMB1_COMMAND_TRANS2_SECONDARY => "SMB1_COMMAND_TRANS2_SECONDARY", |
112 | 27 | SMB1_COMMAND_FIND_CLOSE2 => "SMB1_COMMAND_FIND_CLOSE2", |
113 | 109 | SMB1_COMMAND_TREE_DISCONNECT => "SMB1_COMMAND_TREE_DISCONNECT", |
114 | 1.98k | SMB1_COMMAND_NEGOTIATE_PROTOCOL => "SMB1_COMMAND_NEGOTIATE_PROTOCOL", |
115 | 1.91k | SMB1_COMMAND_SESSION_SETUP_ANDX => "SMB1_COMMAND_SESSION_SETUP_ANDX", |
116 | 40 | SMB1_COMMAND_LOGOFF_ANDX => "SMB1_COMMAND_LOGOFF_ANDX", |
117 | 1.13k | SMB1_COMMAND_TREE_CONNECT_ANDX => "SMB1_COMMAND_TREE_CONNECT_ANDX", |
118 | 3 | SMB1_COMMAND_QUERY_INFO_DISK => "SMB1_COMMAND_QUERY_INFO_DISK", |
119 | 516 | SMB1_COMMAND_NT_TRANS => "SMB1_COMMAND_NT_TRANS", |
120 | 23 | SMB1_COMMAND_NT_TRANS_SECONDARY => "SMB1_COMMAND_NT_TRANS_SECONDARY", |
121 | 39.7k | SMB1_COMMAND_NT_CREATE_ANDX => "SMB1_COMMAND_NT_CREATE_ANDX", |
122 | 105 | SMB1_COMMAND_NT_CANCEL => "SMB1_COMMAND_NT_CANCEL", |
123 | 35 | _ => { return (c).to_string(); }, |
124 | 62.0k | }.to_string() |
125 | 62.0k | } |
126 | | |
127 | | // later we'll use this to determine if we need to |
128 | | // track a ssn per type |
129 | 1.78M | pub fn smb1_create_new_tx(cmd: u8) -> bool { |
130 | 1.78M | match cmd { |
131 | | SMB1_COMMAND_READ_ANDX | |
132 | | SMB1_COMMAND_WRITE_ANDX | |
133 | | SMB1_COMMAND_TRANS | |
134 | 761k | SMB1_COMMAND_TRANS2 => { false }, |
135 | 1.02M | _ => { true }, |
136 | | } |
137 | 1.78M | } |
138 | | |
139 | | // see if we're going to do a lookup for a TX. |
140 | | // related to smb1_create_new_tx(), however it |
141 | | // excludes the 'maybe' commands like TRANS2 |
142 | 152k | pub fn smb1_check_tx(cmd: u8) -> bool { |
143 | 152k | match cmd { |
144 | | SMB1_COMMAND_READ_ANDX | |
145 | | SMB1_COMMAND_WRITE_ANDX | |
146 | 1.68k | SMB1_COMMAND_TRANS => { false }, |
147 | 150k | _ => { true }, |
148 | | } |
149 | 152k | } |
150 | | |
151 | 184k | fn smb1_close_file(state: &mut SMBState, fid: &[u8], direction: Direction) |
152 | | { |
153 | 184k | if let Some(tx) = state.get_file_tx_by_fuid(fid, direction) { |
154 | | SCLogDebug!("found tx {}", tx.id); |
155 | 140k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
156 | 140k | if !tx.request_done { |
157 | 59.2k | SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid); |
158 | 59.2k | filetracker_close(&mut tdf.file_tracker); |
159 | 59.2k | tx.request_done = true; |
160 | 59.2k | tx.response_done = true; |
161 | 59.2k | SCLogDebug!("tx {} is done", tx.id); |
162 | 81.3k | } |
163 | 0 | } |
164 | 44.2k | } |
165 | 184k | } |
166 | | |
167 | 2.57M | fn smb1_command_is_andx(c: u8) -> bool { |
168 | 2.57M | match c { |
169 | | SMB1_COMMAND_LOCKING_ANDX | |
170 | | SMB1_COMMAND_OPEN_ANDX | |
171 | | SMB1_COMMAND_READ_ANDX | |
172 | | SMB1_COMMAND_SESSION_SETUP_ANDX | |
173 | | SMB1_COMMAND_LOGOFF_ANDX | |
174 | | SMB1_COMMAND_TREE_CONNECT_ANDX | |
175 | | SMB1_COMMAND_NT_CREATE_ANDX | |
176 | | SMB1_COMMAND_WRITE_ANDX => { |
177 | 1.38M | return true; |
178 | | } |
179 | | _ => { |
180 | 1.18M | return false; |
181 | | } |
182 | | } |
183 | 2.57M | } |
184 | | |
185 | 2.14M | fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, andx_offset: &mut usize) { |
186 | 2.14M | let mut events : Vec<SMBEvent> = Vec::new(); |
187 | 2.14M | let mut no_response_expected = false; |
188 | | |
189 | 2.14M | let have_tx = match command { |
190 | | SMB1_COMMAND_RENAME => { |
191 | 94.0k | match parse_smb_rename_request_record(r.data) { |
192 | 43.0k | Ok((_, rd)) => { |
193 | | SCLogDebug!("RENAME {:?}", rd); |
194 | | |
195 | 43.0k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
196 | 43.0k | let mut newname = rd.newname; |
197 | 184k | newname.retain(|&i|i != 0x00); |
198 | 43.0k | let mut oldname = rd.oldname; |
199 | 240k | oldname.retain(|&i|i != 0x00); |
200 | | |
201 | 43.0k | let tx = state.new_rename_tx(Vec::new(), oldname, newname); |
202 | 43.0k | tx.hdr = tx_hdr; |
203 | 43.0k | tx.request_done = true; |
204 | 43.0k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_RENAME); |
205 | 43.0k | true |
206 | | }, |
207 | | _ => { |
208 | 50.9k | events.push(SMBEvent::MalformedData); |
209 | 50.9k | false |
210 | | }, |
211 | | } |
212 | | }, |
213 | | SMB1_COMMAND_TRANS2 => { |
214 | 63.2k | match parse_smb_trans2_request_record(r.data) { |
215 | 37.3k | Ok((_, rd)) => { |
216 | | SCLogDebug!("TRANS2 DONE {:?}", rd); |
217 | | |
218 | 37.3k | if rd.subcmd == 6 { |
219 | | SCLogDebug!("SET_PATH_INFO"); |
220 | 8.06k | match parse_trans2_request_params_set_path_info(rd.setup_blob) { |
221 | 5.84k | Ok((_, pd)) => { |
222 | | SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS DONE {:?}", pd); |
223 | | |
224 | 5.84k | if pd.loi == 1013 { // set disposition info |
225 | 1.38k | match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) { |
226 | 1.10k | Ok((_, disp)) => { |
227 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp); |
228 | 1.10k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
229 | | |
230 | 1.10k | let tx = state.new_setpathinfo_tx(pd.oldname, |
231 | 1.10k | rd.subcmd, pd.loi, disp.delete); |
232 | 1.10k | tx.hdr = tx_hdr; |
233 | 1.10k | tx.request_done = true; |
234 | 1.10k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); |
235 | 1.10k | true |
236 | | |
237 | | }, |
238 | 283 | Err(Err::Incomplete(_n)) => { |
239 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n); |
240 | 283 | events.push(SMBEvent::MalformedData); |
241 | 283 | false |
242 | | }, |
243 | 0 | Err(Err::Error(_e)) | |
244 | 0 | Err(Err::Failure(_e)) => { |
245 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e); |
246 | 0 | events.push(SMBEvent::MalformedData); |
247 | 0 | false |
248 | | }, |
249 | | } |
250 | 4.46k | } else if pd.loi == 1010 { |
251 | 3.32k | match parse_trans2_request_data_set_path_info_rename(rd.data_blob) { |
252 | 1.32k | Ok((_, ren)) => { |
253 | | SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME DONE {:?}", ren); |
254 | 1.32k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
255 | 1.32k | let mut newname = ren.newname.to_vec(); |
256 | 4.46k | newname.retain(|&i|i != 0x00); |
257 | | |
258 | 1.32k | let fid : Vec<u8> = Vec::new(); |
259 | | |
260 | 1.32k | let tx = state.new_rename_tx(fid, pd.oldname, newname); |
261 | 1.32k | tx.hdr = tx_hdr; |
262 | 1.32k | tx.request_done = true; |
263 | 1.32k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); |
264 | 1.32k | true |
265 | | }, |
266 | 1.99k | Err(Err::Incomplete(_n)) => { |
267 | | SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME INCOMPLETE {:?}", _n); |
268 | 1.99k | events.push(SMBEvent::MalformedData); |
269 | 1.99k | false |
270 | | }, |
271 | 0 | Err(Err::Error(_e)) | |
272 | 0 | Err(Err::Failure(_e)) => { |
273 | | SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME ERROR {:?}", _e); |
274 | 0 | events.push(SMBEvent::MalformedData); |
275 | 0 | false |
276 | | }, |
277 | | } |
278 | | } else { |
279 | 1.14k | false |
280 | | } |
281 | | }, |
282 | 1.10k | Err(Err::Incomplete(_n)) => { |
283 | | SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS INCOMPLETE {:?}", _n); |
284 | 1.10k | events.push(SMBEvent::MalformedData); |
285 | 1.10k | false |
286 | | }, |
287 | 1.10k | Err(Err::Error(_e)) | |
288 | 0 | Err(Err::Failure(_e)) => { |
289 | | SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS ERROR {:?}", _e); |
290 | 1.10k | events.push(SMBEvent::MalformedData); |
291 | 1.10k | false |
292 | | }, |
293 | | } |
294 | 29.3k | } else if rd.subcmd == 8 { |
295 | | SCLogDebug!("SET_FILE_INFO"); |
296 | 8.42k | match parse_trans2_request_params_set_file_info(rd.setup_blob) { |
297 | 7.68k | Ok((_, pd)) => { |
298 | | SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS DONE {:?}", pd); |
299 | | |
300 | 7.68k | if pd.loi == 1013 { // set disposition info |
301 | 2.02k | match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) { |
302 | 1.82k | Ok((_, disp)) => { |
303 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp); |
304 | 1.82k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
305 | | |
306 | 1.82k | let mut frankenfid = pd.fid.to_vec(); |
307 | 1.82k | frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
308 | | |
309 | 1.82k | let filename = match state.guid2name_map.get(&frankenfid) { |
310 | 194 | Some(n) => n.to_vec(), |
311 | 1.63k | None => b"<unknown>".to_vec(), |
312 | | }; |
313 | 1.82k | let tx = state.new_setfileinfo_tx(filename, pd.fid.to_vec(), |
314 | 1.82k | rd.subcmd, pd.loi, disp.delete); |
315 | 1.82k | tx.hdr = tx_hdr; |
316 | 1.82k | tx.request_done = true; |
317 | 1.82k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); |
318 | 1.82k | true |
319 | | |
320 | | }, |
321 | 198 | Err(Err::Incomplete(_n)) => { |
322 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n); |
323 | 198 | events.push(SMBEvent::MalformedData); |
324 | 198 | false |
325 | | }, |
326 | 0 | Err(Err::Error(_e)) | |
327 | 0 | Err(Err::Failure(_e)) => { |
328 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e); |
329 | 0 | events.push(SMBEvent::MalformedData); |
330 | 0 | false |
331 | | }, |
332 | | } |
333 | 5.66k | } else if pd.loi == 1010 { |
334 | 4.22k | match parse_trans2_request_data_set_file_info_rename(rd.data_blob) { |
335 | 2.53k | Ok((_, ren)) => { |
336 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME DONE {:?}", ren); |
337 | 2.53k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
338 | 2.53k | let mut newname = ren.newname.to_vec(); |
339 | 48.0k | newname.retain(|&i|i != 0x00); |
340 | | |
341 | 2.53k | let mut frankenfid = pd.fid.to_vec(); |
342 | 2.53k | frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
343 | | |
344 | 2.53k | let oldname = match state.guid2name_map.get(&frankenfid) { |
345 | 264 | Some(n) => n.to_vec(), |
346 | 2.26k | None => b"<unknown>".to_vec(), |
347 | | }; |
348 | 2.53k | let tx = state.new_rename_tx(pd.fid.to_vec(), oldname, newname); |
349 | 2.53k | tx.hdr = tx_hdr; |
350 | 2.53k | tx.request_done = true; |
351 | 2.53k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); |
352 | 2.53k | true |
353 | | }, |
354 | 1.69k | Err(Err::Incomplete(_n)) => { |
355 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME INCOMPLETE {:?}", _n); |
356 | 1.69k | events.push(SMBEvent::MalformedData); |
357 | 1.69k | false |
358 | | }, |
359 | 0 | Err(Err::Error(_e)) | |
360 | 0 | Err(Err::Failure(_e)) => { |
361 | | SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME ERROR {:?}", _e); |
362 | 0 | events.push(SMBEvent::MalformedData); |
363 | 0 | false |
364 | | }, |
365 | | } |
366 | | } else { |
367 | 1.43k | false |
368 | | } |
369 | | }, |
370 | 742 | Err(Err::Incomplete(_n)) => { |
371 | | SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS INCOMPLETE {:?}", _n); |
372 | 742 | events.push(SMBEvent::MalformedData); |
373 | 742 | false |
374 | | }, |
375 | 0 | Err(Err::Error(_e)) | |
376 | 0 | Err(Err::Failure(_e)) => { |
377 | | SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS ERROR {:?}", _e); |
378 | 0 | events.push(SMBEvent::MalformedData); |
379 | 0 | false |
380 | | }, |
381 | | } |
382 | | } else { |
383 | 20.8k | false |
384 | | } |
385 | | }, |
386 | 24.6k | Err(Err::Incomplete(_n)) => { |
387 | | SCLogDebug!("TRANS2 INCOMPLETE {:?}", _n); |
388 | 24.6k | events.push(SMBEvent::MalformedData); |
389 | 24.6k | false |
390 | | }, |
391 | 1.22k | Err(Err::Error(_e)) | |
392 | 0 | Err(Err::Failure(_e)) => { |
393 | | SCLogDebug!("TRANS2 ERROR {:?}", _e); |
394 | 1.22k | events.push(SMBEvent::MalformedData); |
395 | 1.22k | false |
396 | | }, |
397 | | } |
398 | | }, |
399 | | SMB1_COMMAND_READ_ANDX => { |
400 | 127k | match parse_smb_read_andx_request_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) { |
401 | 114k | Ok((_, rr)) => { |
402 | 114k | SCLogDebug!("rr {:?}", rr); |
403 | 114k | |
404 | 114k | // store read fid,offset in map |
405 | 114k | let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET); |
406 | 114k | let mut fid = rr.fid.to_vec(); |
407 | 114k | fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
408 | 114k | let fidoff = SMBFileGUIDOffset::new(fid, rr.offset); |
409 | 114k | state.ssn2vecoffset_map.insert(fid_key, fidoff); |
410 | 114k | }, |
411 | 13.3k | _ => { |
412 | 13.3k | events.push(SMBEvent::MalformedData); |
413 | 13.3k | }, |
414 | | } |
415 | 127k | false |
416 | | }, |
417 | | SMB1_COMMAND_WRITE_ANDX | |
418 | | SMB1_COMMAND_WRITE | |
419 | | SMB1_COMMAND_WRITE_AND_CLOSE => { |
420 | 412k | smb1_write_request_record(state, r, *andx_offset, command, 0); |
421 | 412k | true // tx handling in func |
422 | | }, |
423 | | SMB1_COMMAND_TRANS => { |
424 | 401k | smb1_trans_request_record(state, r); |
425 | 401k | true |
426 | | }, |
427 | | SMB1_COMMAND_NEGOTIATE_PROTOCOL => { |
428 | 19.4k | match parse_smb1_negotiate_protocol_record(r.data) { |
429 | 16.2k | Ok((_, pr)) => { |
430 | | SCLogDebug!("SMB_COMMAND_NEGOTIATE_PROTOCOL {:?}", pr); |
431 | | |
432 | 16.2k | let mut bad_dialects = false; |
433 | 16.2k | let mut dialects : Vec<Vec<u8>> = Vec::new(); |
434 | 2.20M | for d in &pr.dialects { |
435 | 2.18M | if d.is_empty() { |
436 | 174k | bad_dialects = true; |
437 | 174k | continue; |
438 | 2.01M | } else if d.len() == 1 { |
439 | 1.46M | bad_dialects = true; |
440 | 1.46M | } |
441 | 2.01M | let x = &d[1..d.len()]; |
442 | 2.01M | let dvec = x.to_vec(); |
443 | 2.01M | dialects.push(dvec); |
444 | | } |
445 | | |
446 | 16.2k | let found = match state.get_negotiate_tx(1) { |
447 | 4.32k | Some(tx) => { |
448 | | SCLogDebug!("WEIRD, should not have NEGOTIATE tx!"); |
449 | 4.32k | tx.set_event(SMBEvent::DuplicateNegotiate); |
450 | 4.32k | true |
451 | | }, |
452 | 11.8k | None => { false }, |
453 | | }; |
454 | 16.2k | if !found { |
455 | 11.8k | let tx = state.new_negotiate_tx(1); |
456 | 11.8k | if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { |
457 | 11.8k | tdn.dialects = dialects; |
458 | 11.8k | } |
459 | 11.8k | tx.request_done = true; |
460 | 11.8k | if bad_dialects { |
461 | 7.88k | tx.set_event(SMBEvent::NegotiateMalformedDialects); |
462 | 7.88k | } |
463 | 4.32k | } |
464 | 16.2k | true |
465 | | }, |
466 | | _ => { |
467 | 3.28k | events.push(SMBEvent::MalformedData); |
468 | 3.28k | false |
469 | | }, |
470 | | } |
471 | | }, |
472 | | SMB1_COMMAND_NT_CREATE_ANDX => { |
473 | 101k | match parse_smb_create_andx_request_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..], r) { |
474 | 88.7k | Ok((_, cr)) => { |
475 | | SCLogDebug!("Create AndX {:?}", cr); |
476 | 88.7k | let del = cr.create_options & 0x0000_1000 != 0; |
477 | 88.7k | let dir = cr.create_options & 0x0000_0001 != 0; |
478 | | SCLogDebug!("del {} dir {} options {:08x}", del, dir, cr.create_options); |
479 | | |
480 | 88.7k | let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME); |
481 | 88.7k | let name_val = cr.file_name.to_vec(); |
482 | 88.7k | state.ssn2vec_map.insert(name_key, name_val); |
483 | | |
484 | 88.7k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
485 | 88.7k | let tx = state.new_create_tx(&cr.file_name, |
486 | 88.7k | cr.disposition, del, dir, tx_hdr); |
487 | 88.7k | tx.vercmd.set_smb1_cmd(command); |
488 | | SCLogDebug!("TS CREATE TX {} created", tx.id); |
489 | 88.7k | true |
490 | | }, |
491 | | _ => { |
492 | 13.2k | events.push(SMBEvent::MalformedData); |
493 | 13.2k | false |
494 | | }, |
495 | | } |
496 | | }, |
497 | | SMB1_COMMAND_SESSION_SETUP_ANDX => { |
498 | | SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id); |
499 | 154k | smb1_session_setup_request(state, r, *andx_offset); |
500 | 154k | true |
501 | | }, |
502 | | SMB1_COMMAND_TREE_CONNECT_ANDX => { |
503 | | SCLogDebug!("SMB1_COMMAND_TREE_CONNECT_ANDX"); |
504 | 520k | match parse_smb_connect_tree_andx_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..], r) { |
505 | 49.1k | Ok((_, tr)) => { |
506 | 49.1k | let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE); |
507 | 49.1k | let mut name_val = tr.path; |
508 | 49.1k | if name_val.len() > 1 { |
509 | 37.6k | name_val = name_val[1..].to_vec(); |
510 | 37.6k | } |
511 | | |
512 | | // store hdr as SMBHDR_TYPE_TREE, so with tree id 0 |
513 | | // when the response finds this we update it |
514 | 49.1k | let tx = state.new_treeconnect_tx(name_key, name_val); |
515 | 49.1k | if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { |
516 | 49.1k | tdn.req_service = Some(tr.service.to_vec()); |
517 | 49.1k | } |
518 | 49.1k | tx.request_done = true; |
519 | 49.1k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TREE_CONNECT_ANDX); |
520 | 49.1k | true |
521 | | }, |
522 | | _ => { |
523 | 471k | events.push(SMBEvent::MalformedData); |
524 | 471k | false |
525 | | }, |
526 | | } |
527 | | }, |
528 | | SMB1_COMMAND_TREE_DISCONNECT => { |
529 | 19.3k | let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); |
530 | 19.3k | state.ssn2tree_map.remove(&tree_key); |
531 | 19.3k | false |
532 | | }, |
533 | | SMB1_COMMAND_CLOSE => { |
534 | 30.7k | match parse_smb1_close_request_record(r.data) { |
535 | 29.6k | Ok((_, cd)) => { |
536 | 29.6k | let mut fid = cd.fid.to_vec(); |
537 | 29.6k | fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
538 | 29.6k | state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); |
539 | 29.6k | |
540 | 29.6k | SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid); |
541 | 29.6k | smb1_close_file(state, &fid, Direction::ToServer); |
542 | 29.6k | }, |
543 | 1.03k | _ => { |
544 | 1.03k | events.push(SMBEvent::MalformedData); |
545 | 1.03k | }, |
546 | | } |
547 | 30.7k | false |
548 | | }, |
549 | | SMB1_COMMAND_NT_CANCEL | |
550 | | SMB1_COMMAND_TRANS2_SECONDARY | |
551 | | SMB1_COMMAND_LOCKING_ANDX => { |
552 | 33.4k | no_response_expected = true; |
553 | 33.4k | false |
554 | | }, |
555 | | _ => { |
556 | 165k | if command == SMB1_COMMAND_LOGOFF_ANDX || |
557 | 163k | command == SMB1_COMMAND_TREE_DISCONNECT || |
558 | 163k | command == SMB1_COMMAND_NT_TRANS || |
559 | 163k | command == SMB1_COMMAND_NT_TRANS_SECONDARY || |
560 | 162k | command == SMB1_COMMAND_NT_CANCEL || |
561 | 162k | command == SMB1_COMMAND_RENAME || |
562 | 162k | command == SMB1_COMMAND_CHECK_DIRECTORY || |
563 | 162k | command == SMB1_COMMAND_ECHO || |
564 | 162k | command == SMB1_COMMAND_TRANS |
565 | 162k | { } else { |
566 | 162k | SCLogDebug!("unsupported command {}/{}", |
567 | 162k | command, &smb1_command_string(command)); |
568 | 162k | } |
569 | 165k | false |
570 | | }, |
571 | | }; |
572 | 2.14M | if !have_tx && smb1_create_new_tx(command) { |
573 | 788k | let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
574 | 788k | let tx = state.new_generic_tx(1, command as u16, tx_key); |
575 | | SCLogDebug!("tx {} created for {}/{}", tx.id, command, &smb1_command_string(command)); |
576 | 788k | tx.set_events(events); |
577 | 788k | if no_response_expected { |
578 | 33.4k | tx.response_done = true; |
579 | 754k | } |
580 | 1.35M | } |
581 | 2.14M | } |
582 | | |
583 | 1.59M | pub fn smb1_request_record(state: &mut SMBState, r: &SmbRecord) -> u32 { |
584 | | SCLogDebug!("record: command {}: record {:?}", r.command, r); |
585 | | |
586 | 1.59M | let mut andx_offset = SMB1_HEADER_SIZE; |
587 | 1.59M | let mut command = r.command; |
588 | | loop { |
589 | 2.14M | smb1_request_record_one(state, r, command, &mut andx_offset); |
590 | | |
591 | | // continue for next andx command if any |
592 | 2.14M | if smb1_command_is_andx(command) { |
593 | 1.11M | if let Ok((_, andx_hdr)) = smb1_parse_andx_header(&r.data[andx_offset-SMB1_HEADER_SIZE..]) { |
594 | 1.07M | if (andx_hdr.andx_offset as usize) > andx_offset && |
595 | 931k | andx_hdr.andx_command != SMB1_COMMAND_NONE && |
596 | 834k | (andx_hdr.andx_offset as usize) - SMB1_HEADER_SIZE < r.data.len() { |
597 | 554k | andx_offset = andx_hdr.andx_offset as usize; |
598 | 554k | command = andx_hdr.andx_command; |
599 | 554k | continue; |
600 | 515k | } |
601 | 43.9k | } |
602 | 1.03M | } |
603 | 1.59M | break; |
604 | | } |
605 | | |
606 | 1.59M | 0 |
607 | 1.59M | } |
608 | | |
609 | 426k | fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, andx_offset: &mut usize) { |
610 | | SCLogDebug!("record: command {} status {} -> {:?}", r.command, r.nt_status, r); |
611 | | |
612 | 426k | let key_ssn_id = r.ssn_id; |
613 | 426k | let key_tree_id = r.tree_id; |
614 | 426k | let key_multiplex_id = r.multiplex_id; |
615 | 426k | let mut tx_sync = false; |
616 | 426k | let mut events : Vec<SMBEvent> = Vec::new(); |
617 | | |
618 | 426k | let have_tx = match command { |
619 | | SMB1_COMMAND_READ_ANDX => { |
620 | 78.1k | smb1_read_response_record(state, r, *andx_offset, 0); |
621 | 78.1k | true // tx handling in func |
622 | | }, |
623 | | SMB1_COMMAND_NEGOTIATE_PROTOCOL => { |
624 | | SCLogDebug!("SMB1_COMMAND_NEGOTIATE_PROTOCOL response"); |
625 | 18.1k | match parse_smb1_negotiate_protocol_response_record(r.data) { |
626 | 9.22k | Ok((_, pr)) => { |
627 | 9.22k | let (have_ntx, dialect) = match state.get_negotiate_tx(1) { |
628 | 4.11k | Some(tx) => { |
629 | 4.11k | tx.set_status(r.nt_status, r.is_dos_error); |
630 | 4.11k | tx.response_done = true; |
631 | | SCLogDebug!("tx {} is done", tx.id); |
632 | 4.11k | let d = match tx.type_data { |
633 | 4.11k | Some(SMBTransactionTypeData::NEGOTIATE(ref mut x)) => { |
634 | 4.11k | x.server_guid = pr.server_guid.to_vec(); |
635 | | |
636 | 4.11k | let dialect_idx = pr.dialect_idx as usize; |
637 | 4.11k | if x.dialects.len() <= dialect_idx { |
638 | 474 | None |
639 | | } else { |
640 | 3.64k | let d = x.dialects[dialect_idx].to_vec(); |
641 | 3.64k | Some(d) |
642 | | } |
643 | | }, |
644 | 0 | _ => { None }, |
645 | | }; |
646 | 4.11k | if d.is_none() { |
647 | 474 | tx.set_event(SMBEvent::NegotiateMalformedDialects); |
648 | 3.64k | } |
649 | 4.11k | (true, d) |
650 | | }, |
651 | 5.11k | None => { (false, None) }, |
652 | | }; |
653 | 9.22k | if let Some(d) = dialect { |
654 | 3.64k | SCLogDebug!("dialect {:?}", d); |
655 | 3.64k | state.dialect_vec = Some(d); |
656 | 5.58k | } |
657 | 9.22k | have_ntx |
658 | | }, |
659 | | _ => { |
660 | 8.92k | events.push(SMBEvent::MalformedData); |
661 | 8.92k | false |
662 | | }, |
663 | | } |
664 | | }, |
665 | | SMB1_COMMAND_TREE_CONNECT_ANDX => { |
666 | 42.4k | if r.nt_status != SMB_NTSTATUS_SUCCESS { |
667 | 4.68k | let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE); |
668 | 4.68k | if let Some(tx) = state.get_treeconnect_tx(name_key) { |
669 | 645 | if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { |
670 | 645 | tdn.tree_id = r.tree_id as u32; |
671 | 645 | } |
672 | 645 | tx.set_status(r.nt_status, r.is_dos_error); |
673 | 645 | tx.response_done = true; |
674 | | SCLogDebug!("tx {} is done", tx.id); |
675 | 4.04k | } |
676 | 4.68k | return; |
677 | 37.7k | } |
678 | | |
679 | 37.7k | match parse_smb_connect_tree_andx_response_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) { |
680 | 25.2k | Ok((_, tr)) => { |
681 | 25.2k | let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE); |
682 | 25.2k | let is_pipe = tr.service == "IPC".as_bytes(); |
683 | 25.2k | let mut share_name = Vec::new(); |
684 | 25.2k | let found = match state.get_treeconnect_tx(name_key) { |
685 | 4.67k | Some(tx) => { |
686 | 4.67k | if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { |
687 | 4.67k | tdn.is_pipe = is_pipe; |
688 | 4.67k | tdn.tree_id = r.tree_id as u32; |
689 | 4.67k | share_name = tdn.share_name.to_vec(); |
690 | 4.67k | tdn.res_service = Some(tr.service.to_vec()); |
691 | 4.67k | } |
692 | 4.67k | tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); |
693 | 4.67k | tx.set_status(r.nt_status, r.is_dos_error); |
694 | 4.67k | tx.response_done = true; |
695 | | SCLogDebug!("tx {} is done", tx.id); |
696 | 4.67k | true |
697 | | }, |
698 | 20.5k | None => { false }, |
699 | | }; |
700 | 25.2k | if found { |
701 | 4.67k | let tree = SMBTree::new(share_name.to_vec(), is_pipe); |
702 | 4.67k | let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); |
703 | 4.67k | state.ssn2tree_map.insert(tree_key, tree); |
704 | 20.5k | } |
705 | 25.2k | found |
706 | | }, |
707 | | _ => { |
708 | 12.5k | events.push(SMBEvent::MalformedData); |
709 | 12.5k | false |
710 | | }, |
711 | | } |
712 | | }, |
713 | | SMB1_COMMAND_TREE_DISCONNECT => { |
714 | | // normally removed when processing request, |
715 | | // but in case we missed that try again here |
716 | 5.02k | let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); |
717 | 5.02k | state.ssn2tree_map.remove(&tree_key); |
718 | 5.02k | false |
719 | | }, |
720 | | SMB1_COMMAND_NT_CREATE_ANDX => { |
721 | | SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX response {:08x}", r.nt_status); |
722 | 103k | if r.nt_status == SMB_NTSTATUS_SUCCESS { |
723 | 64.7k | match parse_smb_create_andx_response_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) { |
724 | 47.2k | Ok((_, cr)) => { |
725 | | SCLogDebug!("Create AndX {:?}", cr); |
726 | | |
727 | 47.2k | let guid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME); |
728 | 47.2k | if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { |
729 | 304k | p.retain(|&i|i != 0x00); |
730 | | |
731 | 20.4k | let mut fid = cr.fid.to_vec(); |
732 | 20.4k | fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
733 | | SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX fid {:?}", fid); |
734 | | SCLogDebug!("fid {:?} name {:?}", fid, p); |
735 | 20.4k | state.guid2name_map.insert(fid, p); |
736 | 26.7k | } else { |
737 | 26.7k | SCLogDebug!("SMBv1 response: GUID NOT FOUND"); |
738 | 26.7k | } |
739 | | |
740 | 47.2k | let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
741 | 47.2k | if let Some(tx) = state.get_generic_tx(1, command as u16, &tx_hdr) { |
742 | | SCLogDebug!("tx {} with {}/{} marked as done", |
743 | | tx.id, command, &smb1_command_string(command)); |
744 | 13.5k | tx.set_status(r.nt_status, false); |
745 | 13.5k | tx.response_done = true; |
746 | | |
747 | 12.9k | if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data { |
748 | 12.9k | tdn.create_ts = cr.create_ts.as_unix(); |
749 | 12.9k | tdn.last_access_ts = cr.last_access_ts.as_unix(); |
750 | 12.9k | tdn.last_write_ts = cr.last_write_ts.as_unix(); |
751 | 12.9k | tdn.last_change_ts = cr.last_change_ts.as_unix(); |
752 | 12.9k | tdn.size = cr.file_size; |
753 | 12.9k | tdn.guid = cr.fid.to_vec(); |
754 | 12.9k | } |
755 | 33.6k | } |
756 | 47.2k | true |
757 | | }, |
758 | | _ => { |
759 | 17.5k | events.push(SMBEvent::MalformedData); |
760 | 17.5k | false |
761 | | }, |
762 | | } |
763 | | } else { |
764 | 38.8k | false |
765 | | } |
766 | | }, |
767 | | SMB1_COMMAND_CLOSE => { |
768 | 10.9k | let fid = state.ssn2vec_map.remove(&SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)); |
769 | 10.9k | if let Some(fid) = fid { |
770 | 7.07k | SCLogDebug!("closing FID {:?}", fid); |
771 | 7.07k | smb1_close_file(state, &fid, Direction::ToClient); |
772 | 7.07k | } |
773 | 10.9k | false |
774 | | }, |
775 | | SMB1_COMMAND_TRANS => { |
776 | 97.1k | smb1_trans_response_record(state, r); |
777 | 97.1k | true |
778 | | }, |
779 | | SMB1_COMMAND_SESSION_SETUP_ANDX => { |
780 | 34.2k | smb1_session_setup_response(state, r, *andx_offset); |
781 | 34.2k | true |
782 | | }, |
783 | | SMB1_COMMAND_LOGOFF_ANDX => { |
784 | 4.31k | tx_sync = true; |
785 | 4.31k | false |
786 | | }, |
787 | | _ => { |
788 | 33.0k | false |
789 | | }, |
790 | | }; |
791 | | |
792 | 422k | if !have_tx && tx_sync { |
793 | 4.31k | if let Some(tx) = state.get_last_tx(1, command as u16) { |
794 | 1.53k | SCLogDebug!("last TX {} is CMD {}", tx.id, &smb1_command_string(command)); |
795 | 1.53k | tx.response_done = true; |
796 | 1.53k | SCLogDebug!("tx {} cmd {} is done", tx.id, command); |
797 | 1.53k | tx.set_status(r.nt_status, r.is_dos_error); |
798 | 1.53k | tx.set_events(events); |
799 | 2.78k | } |
800 | 417k | } else if !have_tx && smb1_check_tx(command) { |
801 | 150k | let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX, |
802 | 150k | key_ssn_id as u64, key_tree_id as u32, key_multiplex_id as u64); |
803 | 150k | let _have_tx2 = match state.get_generic_tx(1, command as u16, &tx_key) { |
804 | 42.0k | Some(tx) => { |
805 | 42.0k | tx.request_done = true; |
806 | 42.0k | tx.response_done = true; |
807 | | SCLogDebug!("tx {} cmd {} is done", tx.id, command); |
808 | 42.0k | tx.set_status(r.nt_status, r.is_dos_error); |
809 | 42.0k | tx.set_events(events); |
810 | 42.0k | true |
811 | | }, |
812 | | None => { |
813 | | SCLogDebug!("no TX found for key {:?}", tx_key); |
814 | 108k | false |
815 | | }, |
816 | | }; |
817 | 267k | } else { |
818 | 267k | SCLogDebug!("have tx for cmd {}", command); |
819 | 267k | } |
820 | 426k | } |
821 | | |
822 | 349k | pub fn smb1_response_record(state: &mut SMBState, r: &SmbRecord) -> u32 { |
823 | 349k | let mut andx_offset = SMB1_HEADER_SIZE; |
824 | 349k | let mut command = r.command; |
825 | | loop { |
826 | 426k | smb1_response_record_one(state, r, command, &mut andx_offset); |
827 | | |
828 | | // continue for next andx command if any |
829 | 426k | if smb1_command_is_andx(command) { |
830 | 270k | if let Ok((_, andx_hdr)) = smb1_parse_andx_header(&r.data[andx_offset-SMB1_HEADER_SIZE..]) { |
831 | 216k | if (andx_hdr.andx_offset as usize) > andx_offset && |
832 | 157k | andx_hdr.andx_command != SMB1_COMMAND_NONE && |
833 | 141k | (andx_hdr.andx_offset as usize) - SMB1_HEADER_SIZE < r.data.len() { |
834 | 77.1k | andx_offset = andx_hdr.andx_offset as usize; |
835 | 77.1k | command = andx_hdr.andx_command; |
836 | 77.1k | continue; |
837 | 139k | } |
838 | 53.3k | } |
839 | 156k | } |
840 | 349k | break; |
841 | | } |
842 | | |
843 | 349k | 0 |
844 | 349k | } |
845 | | |
846 | 401k | pub fn smb1_trans_request_record(state: &mut SMBState, r: &SmbRecord) |
847 | | { |
848 | 401k | let mut events : Vec<SMBEvent> = Vec::new(); |
849 | | |
850 | 401k | match parse_smb_trans_request_record(r.data, r) { |
851 | 9.88k | Ok((_, rd)) => { |
852 | | SCLogDebug!("TRANS request {:?}", rd); |
853 | | |
854 | | /* if we have a fid, store it so the response can pick it up */ |
855 | 9.88k | let mut pipe_dcerpc = false; |
856 | 9.88k | if let Some(pipe) = rd.pipe { |
857 | 6.16k | state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), |
858 | 6.16k | pipe.fid.to_vec()); |
859 | 6.16k | |
860 | 6.16k | let mut frankenfid = pipe.fid.to_vec(); |
861 | 6.16k | frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
862 | 6.16k | |
863 | 6.16k | let (_filename, is_dcerpc) = state.get_service_for_guid(&frankenfid); |
864 | 6.16k | |
865 | 6.16k | SCLogDebug!("smb1_trans_request_record: name {} is_dcerpc {}", |
866 | 6.16k | _filename, is_dcerpc); |
867 | 6.16k | pipe_dcerpc = is_dcerpc; |
868 | 6.16k | } |
869 | | |
870 | 9.88k | if pipe_dcerpc { |
871 | 4.56k | SCLogDebug!("SMBv1 TRANS TO PIPE"); |
872 | 4.56k | let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); |
873 | 4.56k | let vercmd = SMBVerCmdStat::new1(r.command); |
874 | 4.56k | smb_write_dcerpc_record(state, vercmd, hdr, rd.data.data); |
875 | 5.32k | } |
876 | | }, |
877 | 391k | _ => { |
878 | 391k | events.push(SMBEvent::MalformedData); |
879 | 391k | }, |
880 | | } |
881 | 401k | smb1_request_record_generic(state, r, events); |
882 | 401k | } |
883 | | |
884 | 97.1k | pub fn smb1_trans_response_record(state: &mut SMBState, r: &SmbRecord) |
885 | | { |
886 | 97.1k | let mut events : Vec<SMBEvent> = Vec::new(); |
887 | | |
888 | 97.1k | match parse_smb_trans_response_record(r.data) { |
889 | 62.6k | Ok((_, rd)) => { |
890 | | SCLogDebug!("TRANS response {:?}", rd); |
891 | | |
892 | | // see if we have a stored fid |
893 | 62.6k | let fid = state.ssn2vec_map.remove( |
894 | 62.6k | &SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)).unwrap_or_default(); |
895 | | SCLogDebug!("FID {:?}", fid); |
896 | | |
897 | 62.6k | let mut frankenfid = fid.to_vec(); |
898 | 62.6k | frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
899 | | |
900 | 62.6k | let (_filename, is_dcerpc) = state.get_service_for_guid(&frankenfid); |
901 | | |
902 | | SCLogDebug!("smb1_trans_response_record: name {} is_dcerpc {}", |
903 | | _filename, is_dcerpc); |
904 | | |
905 | | // if we get status 'BUFFER_OVERFLOW' this is only a part of |
906 | | // the data. Store it in the ssn/tree for later use. |
907 | 62.6k | if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW { |
908 | 50.6k | let key = SMBHashKeyHdrGuid::new(SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid); |
909 | 50.6k | SCLogDebug!("SMBv1/TRANS: queueing data for len {} key {:?}", rd.data.len(), key); |
910 | 50.6k | state.ssnguid2vec_map.insert(key, rd.data.to_vec()); |
911 | 50.6k | } else if is_dcerpc { |
912 | 3.71k | SCLogDebug!("SMBv1 TRANS TO PIPE"); |
913 | 3.71k | let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); |
914 | 3.71k | let vercmd = SMBVerCmdStat::new1_with_ntstatus(r.command, r.nt_status); |
915 | 3.71k | smb_read_dcerpc_record(state, vercmd, hdr, &fid, rd.data); |
916 | 8.27k | } |
917 | | }, |
918 | 34.5k | _ => { |
919 | 34.5k | events.push(SMBEvent::MalformedData); |
920 | 34.5k | }, |
921 | | } |
922 | | |
923 | | // generic tx as well. Set events if needed. |
924 | 97.1k | smb1_response_record_generic(state, r, events); |
925 | 97.1k | } |
926 | | |
927 | | /// Handle WRITE, WRITE_ANDX, WRITE_AND_CLOSE request records |
928 | 414k | pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offset: usize, command: u8, nbss_remaining: u32) |
929 | | { |
930 | 414k | let mut events : Vec<SMBEvent> = Vec::new(); |
931 | | |
932 | 414k | let result = if command == SMB1_COMMAND_WRITE_ANDX { |
933 | 177k | parse_smb1_write_andx_request_record(&r.data[andx_offset-SMB1_HEADER_SIZE..], andx_offset) |
934 | 236k | } else if command == SMB1_COMMAND_WRITE { |
935 | 18.6k | parse_smb1_write_request_record(r.data) |
936 | | } else { |
937 | 217k | parse_smb1_write_and_close_request_record(r.data) |
938 | | }; |
939 | 414k | match result { |
940 | 160k | Ok((_, rd)) => { |
941 | | SCLogDebug!("SMBv1: write andx => {:?}", rd); |
942 | 160k | if rd.len > rd.data.len() as u32 + nbss_remaining { |
943 | | // Record claims more bytes than are in NBSS record... |
944 | 3.61k | state.set_event(SMBEvent::WriteRequestTooLarge); |
945 | | // Skip the remaining bytes of the record. |
946 | 3.61k | state.set_skip(Direction::ToServer, nbss_remaining); |
947 | 3.61k | return; |
948 | 156k | } |
949 | 156k | let mut file_fid = rd.fid.to_vec(); |
950 | 156k | file_fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); |
951 | | SCLogDebug!("SMBv1 WRITE: FID {:?} offset {}", |
952 | | file_fid, rd.offset); |
953 | | |
954 | 156k | let file_name = match state.guid2name_map.get(&file_fid) { |
955 | 2.22k | Some(n) => n.to_vec(), |
956 | 154k | None => b"<unknown>".to_vec(), |
957 | | }; |
958 | 156k | let mut set_event_fileoverlap = false; |
959 | 156k | let found = match state.get_file_tx_by_fuid_with_open_file(&file_fid, Direction::ToServer) { |
960 | 76.7k | Some(tx) => { |
961 | 76.7k | let file_id : u32 = tx.id as u32; |
962 | 76.7k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
963 | 76.7k | if rd.offset < tdf.file_tracker.tracked { |
964 | 940 | set_event_fileoverlap = true; |
965 | 75.7k | } |
966 | 76.7k | filetracker_newchunk(&mut tdf.file_tracker, |
967 | 76.7k | &file_name, rd.data, rd.offset, |
968 | 76.7k | rd.len, false, &file_id); |
969 | | SCLogDebug!("FID {:?} found at tx {} => {:?}", file_fid, tx.id, tx); |
970 | 0 | } |
971 | 76.7k | true |
972 | | }, |
973 | 80.0k | None => { false }, |
974 | | }; |
975 | 156k | if !found { |
976 | 80.0k | let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); |
977 | 80.0k | let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) { |
978 | 13.9k | Some(n) => (n.name.to_vec(), n.is_pipe), |
979 | 66.0k | None => (Vec::new(), false), |
980 | | }; |
981 | 80.0k | if is_pipe { |
982 | 13.1k | SCLogDebug!("SMBv1 WRITE TO PIPE"); |
983 | 13.1k | let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); |
984 | 13.1k | let vercmd = SMBVerCmdStat::new1_with_ntstatus(command, r.nt_status); |
985 | 13.1k | smb_write_dcerpc_record(state, vercmd, hdr, rd.data); |
986 | 13.1k | } else { |
987 | 66.8k | let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToServer); |
988 | 66.8k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
989 | 66.8k | let file_id : u32 = tx.id as u32; |
990 | 66.8k | if rd.offset < tdf.file_tracker.tracked { |
991 | 0 | set_event_fileoverlap = true; |
992 | 66.8k | } |
993 | 66.8k | filetracker_newchunk(&mut tdf.file_tracker, |
994 | 66.8k | &file_name, rd.data, rd.offset, |
995 | 66.8k | rd.len, false, &file_id); |
996 | 66.8k | tdf.share_name = share_name; |
997 | | SCLogDebug!("tdf {:?}", tdf); |
998 | 0 | } |
999 | 66.8k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_WRITE_ANDX); |
1000 | | SCLogDebug!("FID {:?} found at tx {} => {:?}", file_fid, tx.id, tx); |
1001 | | } |
1002 | 76.7k | } |
1003 | 156k | if set_event_fileoverlap { |
1004 | 940 | state.set_event(SMBEvent::FileOverlap); |
1005 | 155k | } |
1006 | | |
1007 | 156k | state.set_file_left(Direction::ToServer, rd.len, rd.data.len() as u32, file_fid.to_vec()); |
1008 | | |
1009 | 156k | if command == SMB1_COMMAND_WRITE_AND_CLOSE { |
1010 | 148k | SCLogDebug!("closing FID {:?}", file_fid); |
1011 | 148k | smb1_close_file(state, &file_fid, Direction::ToServer); |
1012 | 148k | } |
1013 | | }, |
1014 | 253k | _ => { |
1015 | 253k | events.push(SMBEvent::MalformedData); |
1016 | 253k | }, |
1017 | | } |
1018 | 410k | smb1_request_record_generic(state, r, events); |
1019 | 414k | } |
1020 | | |
1021 | 79.9k | pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offset: usize, nbss_remaining: u32) |
1022 | | { |
1023 | 79.9k | let mut events : Vec<SMBEvent> = Vec::new(); |
1024 | | |
1025 | 79.9k | if r.nt_status == SMB_NTSTATUS_SUCCESS { |
1026 | 77.8k | match parse_smb_read_andx_response_record(&r.data[andx_offset-SMB1_HEADER_SIZE..]) { |
1027 | 52.8k | Ok((_, rd)) => { |
1028 | | SCLogDebug!("SMBv1: read response => {:?}", rd); |
1029 | 52.8k | if rd.len > nbss_remaining + rd.data.len() as u32 { |
1030 | | // Record claims more bytes than are in NBSS record... |
1031 | 2.96k | state.set_event(SMBEvent::ReadResponseTooLarge); |
1032 | | // Skip the remaining bytes of the record. |
1033 | 2.96k | state.set_skip(Direction::ToClient, nbss_remaining); |
1034 | 2.96k | return; |
1035 | 49.8k | } |
1036 | 49.8k | let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET); |
1037 | 49.8k | let (offset, file_fid) = match state.ssn2vecoffset_map.remove(&fid_key) { |
1038 | 37.6k | Some(o) => (o.offset, o.guid), |
1039 | | None => { |
1040 | | SCLogDebug!("SMBv1 READ response: reply to unknown request: left {} {:?}", |
1041 | | rd.len - rd.data.len() as u32, rd); |
1042 | 12.2k | state.set_skip(Direction::ToClient, nbss_remaining); |
1043 | 12.2k | return; |
1044 | | }, |
1045 | | }; |
1046 | | SCLogDebug!("SMBv1 READ: FID {:?} offset {}", file_fid, offset); |
1047 | | |
1048 | 37.6k | let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); |
1049 | 37.6k | let (is_pipe, share_name) = match state.ssn2tree_map.get(&tree_key) { |
1050 | 12.6k | Some(n) => (n.is_pipe, n.name.to_vec()), |
1051 | 25.0k | _ => { (false, Vec::new()) }, |
1052 | | }; |
1053 | 37.6k | if !is_pipe { |
1054 | 27.1k | let file_name = match state.guid2name_map.get(&file_fid) { |
1055 | 8.01k | Some(n) => n.to_vec(), |
1056 | 19.1k | None => Vec::new(), |
1057 | | }; |
1058 | 27.1k | let mut set_event_fileoverlap = false; |
1059 | 27.1k | let found = match state.get_file_tx_by_fuid_with_open_file(&file_fid, Direction::ToClient) { |
1060 | 9.31k | Some(tx) => { |
1061 | 9.31k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
1062 | 9.31k | let file_id : u32 = tx.id as u32; |
1063 | | SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id); |
1064 | 9.31k | if offset < tdf.file_tracker.tracked { |
1065 | 4.99k | set_event_fileoverlap = true; |
1066 | 4.99k | } |
1067 | 9.31k | filetracker_newchunk(&mut tdf.file_tracker, |
1068 | 9.31k | &file_name, rd.data, offset, |
1069 | 9.31k | rd.len, false, &file_id); |
1070 | 0 | } |
1071 | 9.31k | true |
1072 | | }, |
1073 | 17.8k | None => { false }, |
1074 | | }; |
1075 | 27.1k | if !found { |
1076 | 17.8k | let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToClient); |
1077 | 17.8k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
1078 | 17.8k | let file_id : u32 = tx.id as u32; |
1079 | | SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id); |
1080 | 17.8k | if offset < tdf.file_tracker.tracked { |
1081 | 0 | set_event_fileoverlap = true; |
1082 | 17.8k | } |
1083 | 17.8k | filetracker_newchunk(&mut tdf.file_tracker, |
1084 | 17.8k | &file_name, rd.data, offset, |
1085 | 17.8k | rd.len, false, &file_id); |
1086 | 17.8k | tdf.share_name = share_name; |
1087 | 0 | } |
1088 | 17.8k | tx.vercmd.set_smb1_cmd(SMB1_COMMAND_READ_ANDX); |
1089 | 9.31k | } |
1090 | 27.1k | if set_event_fileoverlap { |
1091 | 4.99k | state.set_event(SMBEvent::FileOverlap); |
1092 | 22.1k | } |
1093 | | } else { |
1094 | | SCLogDebug!("SMBv1 READ response from PIPE"); |
1095 | 10.4k | let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); |
1096 | 10.4k | let vercmd = SMBVerCmdStat::new1(SMB1_COMMAND_READ_ANDX); |
1097 | | |
1098 | | // hack: we store fid with ssn id mixed in, but here we want the |
1099 | | // real thing instead. |
1100 | 10.4k | let pure_fid = if file_fid.len() > 2 { &file_fid[0..2] } else { &[] }; |
1101 | 10.4k | smb_read_dcerpc_record(state, vercmd, hdr, pure_fid, rd.data); |
1102 | | } |
1103 | | |
1104 | 37.6k | state.set_file_left(Direction::ToClient, rd.len, rd.data.len() as u32, file_fid.to_vec()); |
1105 | | } |
1106 | 25.0k | _ => { |
1107 | 25.0k | events.push(SMBEvent::MalformedData); |
1108 | 25.0k | }, |
1109 | | } |
1110 | 2.01k | } |
1111 | | |
1112 | | // generic tx as well. Set events if needed. |
1113 | 64.7k | smb1_response_record_generic(state, r, events); |
1114 | 79.9k | } |
1115 | | |
1116 | | /// create a tx for a command / response pair if we're |
1117 | | /// configured to do so, or if this is a tx especially |
1118 | | /// for setting an event. |
1119 | 811k | fn smb1_request_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec<SMBEvent>) { |
1120 | 811k | if smb1_create_new_tx(r.command) || !events.is_empty() { |
1121 | 779k | let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
1122 | 779k | let tx = state.new_generic_tx(1, r.command as u16, tx_key); |
1123 | 779k | tx.set_events(events); |
1124 | 779k | } |
1125 | 811k | } |
1126 | | |
1127 | | /// update or create a tx for a command / response pair based |
1128 | | /// on the response. We only create a tx for the response side |
1129 | | /// if we didn't already update a tx, and we have to set events |
1130 | 161k | fn smb1_response_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec<SMBEvent>) { |
1131 | 161k | let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); |
1132 | 161k | if let Some(tx) = state.get_generic_tx(1, r.command as u16, &tx_key) { |
1133 | 19.0k | tx.request_done = true; |
1134 | 19.0k | tx.response_done = true; |
1135 | | SCLogDebug!("tx {} cmd {} is done", tx.id, r.command); |
1136 | 19.0k | tx.set_status(r.nt_status, r.is_dos_error); |
1137 | 19.0k | tx.set_events(events); |
1138 | 19.0k | return; |
1139 | 142k | } |
1140 | 142k | if !events.is_empty() { |
1141 | 42.5k | let tx = state.new_generic_tx(1, r.command as u16, tx_key); |
1142 | 42.5k | tx.request_done = true; |
1143 | 42.5k | tx.response_done = true; |
1144 | 42.5k | SCLogDebug!("tx {} cmd {} is done", tx.id, r.command); |
1145 | 42.5k | tx.set_status(r.nt_status, r.is_dos_error); |
1146 | 42.5k | tx.set_events(events); |
1147 | 100k | } |
1148 | 161k | } |