/src/suricata7/rust/src/smb/dcerpc.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2017 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 | | // written by Victor Julien |
19 | | |
20 | | use uuid; |
21 | | use crate::smb::smb::*; |
22 | | use crate::smb::smb2::*; |
23 | | use crate::smb::dcerpc_records::*; |
24 | | use crate::smb::events::*; |
25 | | use crate::dcerpc::dcerpc::*; |
26 | | use crate::smb::smb_status::*; |
27 | | |
28 | | impl SMBCommonHdr { |
29 | | /// helper for DCERPC tx tracking. Check if we need |
30 | | /// to use the msg_id/multiplex_id in TX tracking. |
31 | | /// |
32 | 633k | pub fn to_dcerpc(&self, vercmd: &SMBVerCmdStat) -> SMBCommonHdr { |
33 | 633k | // only use the msg id for IOCTL, not for READ/WRITE |
34 | 633k | // as there request/response are different transactions |
35 | 633k | let mut use_msg_id = self.msg_id; |
36 | 633k | match vercmd.get_version() { |
37 | | 2 => { |
38 | 374k | let (_, cmd2) = vercmd.get_smb2_cmd(); |
39 | 374k | let x = match cmd2 { |
40 | 122k | SMB2_COMMAND_READ => { 0 }, |
41 | 245k | SMB2_COMMAND_WRITE => { 0 }, |
42 | 6.88k | SMB2_COMMAND_IOCTL => { self.msg_id }, |
43 | 0 | _ => { self.msg_id }, |
44 | | }; |
45 | 374k | use_msg_id = x; |
46 | | }, |
47 | 258k | 1 => { |
48 | 258k | SCLogDebug!("FIXME TODO"); |
49 | 258k | //let (_, cmd1) = vercmd.get_smb1_cmd(); |
50 | 258k | //if cmd1 != SMB1_COMMAND_IOCTL { |
51 | 258k | use_msg_id = 0; |
52 | 258k | //} |
53 | 258k | }, |
54 | 0 | _ => { }, |
55 | | } |
56 | 633k | SMBCommonHdr { |
57 | 633k | ssn_id: self.ssn_id, |
58 | 633k | tree_id: self.tree_id, |
59 | 633k | msg_id: use_msg_id, |
60 | 633k | rec_type: SMBHDR_TYPE_DCERPCTX, |
61 | 633k | } |
62 | 633k | } |
63 | | } |
64 | | |
65 | | #[derive(Default, Debug)] |
66 | | pub struct DCERPCIface { |
67 | | pub uuid: Vec<u8>, |
68 | | pub ver: u16, |
69 | | pub ver_min: u16, |
70 | | pub ack_result: u16, |
71 | | pub ack_reason: u16, |
72 | | pub acked: bool, |
73 | | pub context_id: u16, |
74 | | } |
75 | | |
76 | | impl DCERPCIface { |
77 | 22.0k | pub fn new(uuid: Vec<u8>, ver: u16, ver_min: u16) -> Self { |
78 | 22.0k | Self { |
79 | 22.0k | uuid, |
80 | 22.0k | ver, |
81 | 22.0k | ver_min, |
82 | 22.0k | ..Default::default() |
83 | 22.0k | } |
84 | 22.0k | } |
85 | | } |
86 | | |
87 | | #[derive(Default, Debug)] |
88 | | pub struct SMBTransactionDCERPC { |
89 | | pub opnum: u16, |
90 | | pub context_id: u16, |
91 | | pub req_cmd: u8, |
92 | | pub req_set: bool, |
93 | | pub res_cmd: u8, |
94 | | pub res_set: bool, |
95 | | pub call_id: u32, |
96 | | pub frag_cnt_ts: u16, |
97 | | pub frag_cnt_tc: u16, |
98 | | pub stub_data_ts: Vec<u8>, |
99 | | pub stub_data_tc: Vec<u8>, |
100 | | } |
101 | | |
102 | | impl SMBTransactionDCERPC { |
103 | 43.7k | fn new_request(req: u8, call_id: u32) -> Self { |
104 | 43.7k | return Self { |
105 | 43.7k | opnum: 0, |
106 | 43.7k | context_id: 0, |
107 | 43.7k | req_cmd: req, |
108 | 43.7k | req_set: true, |
109 | 43.7k | call_id, |
110 | 43.7k | ..Default::default() |
111 | 43.7k | } |
112 | 43.7k | } |
113 | 4.89k | fn new_response(call_id: u32) -> Self { |
114 | 4.89k | return Self { |
115 | 4.89k | call_id, |
116 | 4.89k | ..Default::default() |
117 | 4.89k | }; |
118 | 4.89k | } |
119 | 7.03k | pub fn set_result(&mut self, res: u8) { |
120 | 7.03k | self.res_set = true; |
121 | 7.03k | self.res_cmd = res; |
122 | 7.03k | } |
123 | | } |
124 | | |
125 | | impl SMBState { |
126 | 43.7k | fn new_dcerpc_tx(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32) |
127 | 43.7k | -> &mut SMBTransaction |
128 | 43.7k | { |
129 | 43.7k | let mut tx = self.new_tx(); |
130 | 43.7k | tx.hdr = hdr; |
131 | 43.7k | tx.vercmd = vercmd; |
132 | 43.7k | tx.type_data = Some(SMBTransactionTypeData::DCERPC( |
133 | 43.7k | SMBTransactionDCERPC::new_request(cmd, call_id))); |
134 | 43.7k | |
135 | 43.7k | SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr); |
136 | 43.7k | self.transactions.push_back(tx); |
137 | 43.7k | let tx_ref = self.transactions.back_mut(); |
138 | 43.7k | return tx_ref.unwrap(); |
139 | 43.7k | } |
140 | | |
141 | 4.89k | fn new_dcerpc_tx_for_response(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32) |
142 | 4.89k | -> &mut SMBTransaction |
143 | 4.89k | { |
144 | 4.89k | let mut tx = self.new_tx(); |
145 | 4.89k | tx.hdr = hdr; |
146 | 4.89k | tx.vercmd = vercmd; |
147 | 4.89k | tx.type_data = Some(SMBTransactionTypeData::DCERPC( |
148 | 4.89k | SMBTransactionDCERPC::new_response(call_id))); |
149 | 4.89k | |
150 | 4.89k | SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr); |
151 | 4.89k | self.transactions.push_back(tx); |
152 | 4.89k | let tx_ref = self.transactions.back_mut(); |
153 | 4.89k | return tx_ref.unwrap(); |
154 | 4.89k | } |
155 | | |
156 | 25.5k | fn get_dcerpc_tx(&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32) |
157 | 25.5k | -> Option<&mut SMBTransaction> |
158 | 25.5k | { |
159 | 25.5k | let dce_hdr = hdr.to_dcerpc(vercmd); |
160 | | |
161 | | SCLogDebug!("looking for {:?}", dce_hdr); |
162 | 617k | for tx in &mut self.transactions { |
163 | 608k | let found = dce_hdr.compare(&tx.hdr.to_dcerpc(vercmd)) && |
164 | 43.8k | match tx.type_data { |
165 | 39.5k | Some(SMBTransactionTypeData::DCERPC(ref x)) => { |
166 | 39.5k | x.call_id == call_id |
167 | | }, |
168 | 196k | _ => { false }, |
169 | | }; |
170 | 608k | if found { |
171 | 15.7k | tx.tx_data.updated_tc = true; |
172 | 15.7k | tx.tx_data.updated_ts = true; |
173 | 15.7k | return Some(tx); |
174 | 592k | } |
175 | | } |
176 | 9.82k | return None; |
177 | 25.5k | } |
178 | | } |
179 | | |
180 | | /// Handle DCERPC request data from a WRITE, IOCTL or TRANS record. |
181 | | /// return bool indicating whether an tx has been created/updated. |
182 | | /// |
183 | 73.2k | pub fn smb_write_dcerpc_record(state: &mut SMBState, |
184 | 73.2k | vercmd: SMBVerCmdStat, |
185 | 73.2k | hdr: SMBCommonHdr, |
186 | 73.2k | data: &[u8]) -> bool |
187 | 73.2k | { |
188 | 73.2k | let mut bind_ifaces : Option<Vec<DCERPCIface>> = None; |
189 | 73.2k | let mut is_bind = false; |
190 | 73.2k | |
191 | 73.2k | SCLogDebug!("called for {} bytes of data", data.len()); |
192 | 73.2k | match parse_dcerpc_record(data) { |
193 | 67.3k | Ok((_, dcer)) => { |
194 | 67.3k | SCLogDebug!("DCERPC: version {}.{} write data {} => {:?}", |
195 | 67.3k | dcer.version_major, dcer.version_minor, dcer.data.len(), dcer); |
196 | 67.3k | |
197 | 67.3k | /* if this isn't the first frag, simply update the existing |
198 | 67.3k | * tx with the additional stub data */ |
199 | 67.3k | if dcer.packet_type == DCERPC_TYPE_REQUEST && !dcer.first_frag { |
200 | | SCLogDebug!("NOT the first frag. Need to find an existing TX"); |
201 | 23.6k | match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian) { |
202 | 10.2k | Ok((_, recr)) => { |
203 | 10.2k | let found = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) { |
204 | 6.47k | Some(tx) => { |
205 | | SCLogDebug!("previous CMD {} found at tx {} => {:?}", |
206 | | dcer.packet_type, tx.id, tx); |
207 | 6.47k | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
208 | 6.47k | SCLogDebug!("additional frag of size {}", recr.data.len()); |
209 | 6.47k | tdn.stub_data_ts.extend_from_slice(recr.data); |
210 | 6.47k | tdn.frag_cnt_ts += 1; |
211 | 6.47k | SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); |
212 | 6.47k | } |
213 | 6.47k | if dcer.last_frag { |
214 | 3.81k | SCLogDebug!("last frag set, so request side of DCERPC closed"); |
215 | 3.81k | tx.request_done = true; |
216 | 3.81k | } else { |
217 | 2.66k | SCLogDebug!("NOT last frag, so request side of DCERPC remains open"); |
218 | 2.66k | } |
219 | 6.47k | true |
220 | | }, |
221 | | None => { |
222 | | SCLogDebug!("NO previous CMD {} found", dcer.packet_type); |
223 | 3.72k | false |
224 | | }, |
225 | | }; |
226 | 10.2k | return found; |
227 | | }, |
228 | | _ => { |
229 | 13.4k | state.set_event(SMBEvent::MalformedData); |
230 | 13.4k | return false; |
231 | | }, |
232 | | } |
233 | 43.7k | } |
234 | 43.7k | |
235 | 43.7k | let tx = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id); |
236 | 43.7k | match dcer.packet_type { |
237 | | DCERPC_TYPE_REQUEST => { |
238 | 10.5k | match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian) { |
239 | 5.62k | Ok((_, recr)) => { |
240 | | SCLogDebug!("DCERPC: REQUEST {:?}", recr); |
241 | 5.62k | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
242 | 5.62k | SCLogDebug!("first frag size {}", recr.data.len()); |
243 | 5.62k | tdn.stub_data_ts.extend_from_slice(recr.data); |
244 | 5.62k | tdn.opnum = recr.opnum; |
245 | 5.62k | tdn.context_id = recr.context_id; |
246 | 5.62k | tdn.frag_cnt_ts += 1; |
247 | 5.62k | SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", |
248 | 5.62k | tdn.opnum, tdn.stub_data_ts.len()); |
249 | 5.62k | } |
250 | 5.62k | if dcer.last_frag { |
251 | 823 | tx.request_done = true; |
252 | 4.80k | } else { |
253 | 4.80k | SCLogDebug!("NOT last frag, so request side of DCERPC remains open"); |
254 | 4.80k | } |
255 | | }, |
256 | 4.88k | _ => { |
257 | 4.88k | tx.set_event(SMBEvent::MalformedData); |
258 | 4.88k | tx.request_done = true; |
259 | 4.88k | }, |
260 | | } |
261 | | }, |
262 | | DCERPC_TYPE_BIND => { |
263 | 28.9k | let brec = if dcer.little_endian { |
264 | 19.1k | parse_dcerpc_bind_record(dcer.data) |
265 | | } else { |
266 | 9.77k | parse_dcerpc_bind_record_big(dcer.data) |
267 | | }; |
268 | 28.9k | match brec { |
269 | 2.93k | Ok((_, bindr)) => { |
270 | 2.93k | is_bind = true; |
271 | 2.93k | SCLogDebug!("SMB DCERPC {:?} BIND {:?}", dcer, bindr); |
272 | 2.93k | |
273 | 2.93k | if !bindr.ifaces.is_empty() { |
274 | 1.51k | let mut ifaces: Vec<DCERPCIface> = Vec::new(); |
275 | 23.5k | for i in bindr.ifaces { |
276 | 22.0k | let x = if dcer.little_endian { |
277 | 14.2k | vec![i.iface[3], i.iface[2], i.iface[1], i.iface[0], |
278 | 14.2k | i.iface[5], i.iface[4], i.iface[7], i.iface[6], |
279 | 14.2k | i.iface[8], i.iface[9], i.iface[10], i.iface[11], |
280 | 14.2k | i.iface[12], i.iface[13], i.iface[14], i.iface[15]] |
281 | | } else { |
282 | 7.80k | i.iface.to_vec() |
283 | | }; |
284 | 22.0k | let uuid_str = uuid::Uuid::from_slice(&x.clone()); |
285 | 22.0k | let _uuid_str = uuid_str.map(|uuid_str| uuid_str.to_hyphenated().to_string()).unwrap(); |
286 | 22.0k | let d = DCERPCIface::new(x,i.ver,i.ver_min); |
287 | 22.0k | SCLogDebug!("UUID {} version {}/{} bytes {:?}", |
288 | 22.0k | _uuid_str, |
289 | 22.0k | i.ver, i.ver_min,i.iface); |
290 | 22.0k | ifaces.push(d); |
291 | 22.0k | } |
292 | 1.51k | bind_ifaces = Some(ifaces); |
293 | 1.42k | } |
294 | | }, |
295 | 25.9k | _ => { |
296 | 25.9k | tx.set_event(SMBEvent::MalformedData); |
297 | 25.9k | }, |
298 | | } |
299 | 28.9k | tx.request_done = true; |
300 | | } |
301 | 2.39k | 21..=255 => { |
302 | 2.39k | tx.set_event(SMBEvent::MalformedData); |
303 | 2.39k | tx.request_done = true; |
304 | 2.39k | }, |
305 | 1.88k | _ => { |
306 | 1.88k | // valid type w/o special processing |
307 | 1.88k | tx.request_done = true; |
308 | 1.88k | }, |
309 | | } |
310 | | }, |
311 | 5.92k | _ => { |
312 | 5.92k | state.set_event(SMBEvent::MalformedData); |
313 | 5.92k | }, |
314 | | } |
315 | | |
316 | 49.6k | if is_bind { |
317 | 2.93k | // We have to write here the interfaces |
318 | 2.93k | // rather than in the BIND block |
319 | 2.93k | // due to borrow issues with the tx mutable reference |
320 | 2.93k | // that is part of the state |
321 | 2.93k | state.dcerpc_ifaces = bind_ifaces; // TODO store per ssn |
322 | 46.7k | } |
323 | 49.6k | return true; |
324 | 73.2k | } |
325 | | |
326 | | /// Update TX for bind ack. Needs to update both tx and state. |
327 | | /// |
328 | 6.16k | fn smb_dcerpc_response_bindack( |
329 | 6.16k | state: &mut SMBState, |
330 | 6.16k | vercmd: SMBVerCmdStat, |
331 | 6.16k | hdr: SMBCommonHdr, |
332 | 6.16k | dcer: &DceRpcRecord, |
333 | 6.16k | ntstatus: u32) |
334 | 6.16k | { |
335 | 6.16k | match parse_dcerpc_bindack_record(dcer.data) { |
336 | 2.10k | Ok((_, bindackr)) => { |
337 | | SCLogDebug!("SMB READ BINDACK {:?}", bindackr); |
338 | | |
339 | 2.10k | let found = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) { |
340 | 904 | Some(tx) => { |
341 | 904 | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
342 | 904 | tdn.set_result(DCERPC_TYPE_BINDACK); |
343 | 904 | } |
344 | 904 | tx.vercmd.set_ntstatus(ntstatus); |
345 | 904 | tx.response_done = true; |
346 | 904 | true |
347 | | }, |
348 | 1.20k | None => false, |
349 | | }; |
350 | 2.10k | if found { |
351 | 904 | if let Some(ref mut ifaces) = state.dcerpc_ifaces { |
352 | 1.73k | for (i, r) in bindackr.results.into_iter().enumerate() { |
353 | 1.73k | if i >= ifaces.len() { |
354 | | // TODO set event: more acks that requests |
355 | 202 | break; |
356 | 1.53k | } |
357 | 1.53k | ifaces[i].ack_result = r.ack_result; |
358 | 1.53k | ifaces[i].acked = true; |
359 | | } |
360 | 252 | } |
361 | 1.20k | } |
362 | | }, |
363 | 4.05k | _ => { |
364 | 4.05k | state.set_event(SMBEvent::MalformedData); |
365 | 4.05k | }, |
366 | | } |
367 | 6.16k | } |
368 | | |
369 | 2.32k | fn smb_read_dcerpc_record_error(state: &mut SMBState, |
370 | 2.32k | hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, ntstatus: u32) |
371 | 2.32k | -> bool |
372 | 2.32k | { |
373 | 2.32k | let ver = vercmd.get_version(); |
374 | 2.32k | let cmd = if ver == 2 { |
375 | 2.32k | let (_, c) = vercmd.get_smb2_cmd(); |
376 | 2.32k | c |
377 | | } else { |
378 | 0 | let (_, c) = vercmd.get_smb1_cmd(); |
379 | 0 | c as u16 |
380 | | }; |
381 | | |
382 | 2.32k | let found = match state.get_generic_tx(ver, cmd, &hdr) { |
383 | 907 | Some(tx) => { |
384 | 907 | SCLogDebug!("found"); |
385 | 907 | tx.set_status(ntstatus, false); |
386 | 907 | tx.response_done = true; |
387 | 907 | true |
388 | | }, |
389 | | None => { |
390 | | SCLogDebug!("NOT found"); |
391 | 1.41k | false |
392 | | }, |
393 | | }; |
394 | 2.32k | return found; |
395 | 2.32k | } |
396 | | |
397 | 13.2k | fn dcerpc_response_handle(tx: &mut SMBTransaction, |
398 | 13.2k | vercmd: SMBVerCmdStat, |
399 | 13.2k | dcer: &DceRpcRecord) |
400 | 13.2k | { |
401 | 13.2k | let (_, ntstatus) = vercmd.get_ntstatus(); |
402 | 13.2k | match dcer.packet_type { |
403 | | DCERPC_TYPE_RESPONSE => { |
404 | 8.47k | match parse_dcerpc_response_record(dcer.data, dcer.frag_len) { |
405 | 1.36k | Ok((_, respr)) => { |
406 | | SCLogDebug!("SMBv1 READ RESPONSE {:?}", respr); |
407 | 1.36k | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
408 | 1.36k | SCLogDebug!("CMD 11 found at tx {}", tx.id); |
409 | 1.36k | tdn.set_result(DCERPC_TYPE_RESPONSE); |
410 | 1.36k | tdn.stub_data_tc.extend_from_slice(respr.data); |
411 | 1.36k | tdn.frag_cnt_tc += 1; |
412 | 1.36k | } |
413 | 1.36k | tx.vercmd.set_ntstatus(ntstatus); |
414 | 1.36k | tx.response_done = dcer.last_frag; |
415 | | }, |
416 | 7.11k | _ => { |
417 | 7.11k | tx.set_event(SMBEvent::MalformedData); |
418 | 7.11k | }, |
419 | | } |
420 | | }, |
421 | 0 | DCERPC_TYPE_BINDACK => { |
422 | 0 | // handled elsewhere |
423 | 0 | }, |
424 | 1.89k | 21..=255 => { |
425 | 1.89k | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
426 | 1.89k | tdn.set_result(dcer.packet_type); |
427 | 1.89k | } |
428 | 1.89k | tx.vercmd.set_ntstatus(ntstatus); |
429 | 1.89k | tx.response_done = true; |
430 | 1.89k | tx.set_event(SMBEvent::MalformedData); |
431 | | } |
432 | | _ => { // valid type w/o special processing |
433 | 2.86k | if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { |
434 | 2.86k | tdn.set_result(dcer.packet_type); |
435 | 2.86k | } |
436 | 2.86k | tx.vercmd.set_ntstatus(ntstatus); |
437 | 2.86k | tx.response_done = true; |
438 | | }, |
439 | | } |
440 | 13.2k | } |
441 | | |
442 | | /// Handle DCERPC reply record. Called for READ, TRANS, IOCTL |
443 | | /// |
444 | 34.2k | pub fn smb_read_dcerpc_record(state: &mut SMBState, |
445 | 34.2k | vercmd: SMBVerCmdStat, |
446 | 34.2k | hdr: SMBCommonHdr, |
447 | 34.2k | guid: &[u8], |
448 | 34.2k | indata: &[u8]) -> bool |
449 | 34.2k | { |
450 | 34.2k | let (_, ntstatus) = vercmd.get_ntstatus(); |
451 | 34.2k | |
452 | 34.2k | if ntstatus != SMB_NTSTATUS_SUCCESS && ntstatus != SMB_NTSTATUS_BUFFER_OVERFLOW { |
453 | 2.32k | return smb_read_dcerpc_record_error(state, hdr, vercmd, ntstatus); |
454 | 31.9k | } |
455 | 31.9k | |
456 | 31.9k | SCLogDebug!("lets first see if we have prior data"); |
457 | 31.9k | // msg_id 0 as this data crosses cmd/reply pairs |
458 | 31.9k | let ehdr = SMBHashKeyHdrGuid::new(SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG, |
459 | 31.9k | hdr.ssn_id, hdr.tree_id, 0_u64), guid.to_vec()); |
460 | 31.9k | let mut prevdata = state.ssnguid2vec_map.remove(&ehdr).unwrap_or_default(); |
461 | 31.9k | SCLogDebug!("indata {} prevdata {}", indata.len(), prevdata.len()); |
462 | 31.9k | prevdata.extend_from_slice(indata); |
463 | 31.9k | let data = prevdata; |
464 | 31.9k | |
465 | 31.9k | let mut malformed = false; |
466 | 31.9k | |
467 | 31.9k | if data.is_empty() { |
468 | | SCLogDebug!("weird: no DCERPC data"); // TODO |
469 | | // TODO set event? |
470 | 565 | return false; |
471 | | |
472 | | } else { |
473 | 31.3k | match parse_dcerpc_record(&data) { |
474 | 28.5k | Ok((_, dcer)) => { |
475 | 28.5k | SCLogDebug!("DCERPC: version {}.{} read data {} => {:?}", |
476 | 28.5k | dcer.version_major, dcer.version_minor, dcer.data.len(), dcer); |
477 | 28.5k | |
478 | 28.5k | if ntstatus == SMB_NTSTATUS_BUFFER_OVERFLOW && data.len() < dcer.frag_len as usize { |
479 | | SCLogDebug!("short record {} < {}: storing partial data in state", |
480 | | data.len(), dcer.frag_len); |
481 | 9.10k | state.ssnguid2vec_map.insert(ehdr, data.to_vec()); |
482 | 9.10k | return true; // TODO review |
483 | 19.4k | } |
484 | 19.4k | |
485 | 19.4k | if dcer.packet_type == DCERPC_TYPE_BINDACK { |
486 | 6.16k | smb_dcerpc_response_bindack(state, vercmd, hdr, &dcer, ntstatus); |
487 | 6.16k | return true; |
488 | 13.2k | } |
489 | | |
490 | 13.2k | let found = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) { |
491 | 8.34k | Some(tx) => { |
492 | 8.34k | dcerpc_response_handle(tx, vercmd.clone(), &dcer); |
493 | 8.34k | true |
494 | | }, |
495 | | None => { |
496 | | SCLogDebug!("no tx"); |
497 | 4.89k | false |
498 | | }, |
499 | | }; |
500 | 13.2k | if !found { |
501 | 4.89k | // pick up DCERPC tx even if we missed the request |
502 | 4.89k | let tx = state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id); |
503 | 4.89k | dcerpc_response_handle(tx, vercmd, &dcer); |
504 | 8.34k | } |
505 | | }, |
506 | 2.88k | _ => { |
507 | 2.88k | malformed = true; |
508 | 2.88k | }, |
509 | | } |
510 | | } |
511 | | |
512 | 16.1k | if malformed { |
513 | 2.88k | state.set_event(SMBEvent::MalformedData); |
514 | 13.2k | } |
515 | | |
516 | 16.1k | return true; |
517 | 34.2k | } |
518 | | |
519 | | /// Try to find out if the input data looks like DCERPC |
520 | 34.4k | pub fn smb_dcerpc_probe(data: &[u8]) -> bool |
521 | 34.4k | { |
522 | 34.4k | if let Ok((_, recr)) = parse_dcerpc_record(data) { |
523 | | SCLogDebug!("SMB: could be DCERPC {:?}", recr); |
524 | 23.2k | if recr.version_major == 5 && recr.version_minor < 3 && |
525 | 17.9k | recr.frag_len > 0 && recr.packet_type <= 20 |
526 | | { |
527 | | SCLogDebug!("SMB: looks like we have dcerpc"); |
528 | 17.6k | return true; |
529 | 5.56k | } |
530 | 11.2k | } |
531 | 16.7k | return false; |
532 | 34.4k | } |