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