/src/suricata8/rust/src/ldap/ldap.rs
Line | Count | Source |
1 | | /* Copyright (C) 2024 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 | | // Author: Giuseppe Longo <giuseppe@glongo.it> |
19 | | // Author: Pierre Chifflier <chifflier@wzdftpd.net> |
20 | | |
21 | | use crate::applayer::{self, *}; |
22 | | use crate::conf::conf_get; |
23 | | use crate::core::*; |
24 | | use crate::direction::Direction; |
25 | | use crate::flow::Flow; |
26 | | use crate::frames::*; |
27 | | use ldap_parser::asn1_rs::ToStatic; |
28 | | use nom7 as nom; |
29 | | use std; |
30 | | use std::collections::VecDeque; |
31 | | use std::ffi::CString; |
32 | | use std::os::raw::{c_char, c_int, c_void}; |
33 | | use suricata_sys::sys::{ |
34 | | AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled, |
35 | | SCAppLayerParserRegisterLogger, SCAppLayerParserStateIssetFlag, |
36 | | SCAppLayerProtoDetectConfProtoDetectionEnabled, SCAppLayerRequestProtocolTLSUpgrade, |
37 | | }; |
38 | | |
39 | | use super::types::*; |
40 | | use ldap_parser::ldap::*; |
41 | | |
42 | | static LDAP_MAX_TX_DEFAULT: usize = 256; |
43 | | static mut LDAP_MAX_TX: usize = LDAP_MAX_TX_DEFAULT; |
44 | | |
45 | | static mut LDAP_MAX_RESPONSES: usize = 1024; |
46 | | |
47 | | pub(super) static mut ALPROTO_LDAP: AppProto = ALPROTO_UNKNOWN; |
48 | | |
49 | | const STARTTLS_OID: &str = "1.3.6.1.4.1.1466.20037"; |
50 | | |
51 | | #[derive(AppLayerFrameType)] |
52 | | pub enum LdapFrameType { |
53 | | Pdu, |
54 | | } |
55 | | |
56 | | #[derive(AppLayerEvent)] |
57 | | enum LdapEvent { |
58 | | TooManyTransactions, |
59 | | InvalidData, |
60 | | RequestNotFound, |
61 | | IncompleteData, |
62 | | TooManyResponses, |
63 | | } |
64 | | |
65 | | #[derive(Debug)] |
66 | | pub struct LdapTransaction { |
67 | | pub tx_id: u64, |
68 | | pub request: Option<LdapMessage<'static>>, |
69 | | pub responses: VecDeque<LdapMessage<'static>>, |
70 | | complete: bool, |
71 | | |
72 | | tx_data: AppLayerTxData, |
73 | | } |
74 | | |
75 | | impl Default for LdapTransaction { |
76 | 0 | fn default() -> Self { |
77 | 0 | Self::new() |
78 | 0 | } |
79 | | } |
80 | | |
81 | | impl LdapTransaction { |
82 | 368k | pub fn new() -> LdapTransaction { |
83 | 368k | Self { |
84 | 368k | tx_id: 0, |
85 | 368k | request: None, |
86 | 368k | responses: VecDeque::new(), |
87 | 368k | complete: false, |
88 | 368k | tx_data: AppLayerTxData::new(), |
89 | 368k | } |
90 | 368k | } |
91 | 5.76k | fn set_event(&mut self, e: LdapEvent) { |
92 | 5.76k | self.tx_data.set_event(e as u8); |
93 | 5.76k | } |
94 | | } |
95 | | |
96 | | impl Transaction for LdapTransaction { |
97 | 24.0M | fn id(&self) -> u64 { |
98 | 24.0M | self.tx_id |
99 | 24.0M | } |
100 | | } |
101 | | |
102 | | #[derive(Default)] |
103 | | pub struct LdapState { |
104 | | state_data: AppLayerStateData, |
105 | | tx_id: u64, |
106 | | transactions: VecDeque<LdapTransaction>, |
107 | | request_frame: Option<Frame>, |
108 | | response_frame: Option<Frame>, |
109 | | request_gap: bool, |
110 | | response_gap: bool, |
111 | | request_tls: bool, |
112 | | has_starttls: bool, |
113 | | } |
114 | | |
115 | | impl State<LdapTransaction> for LdapState { |
116 | 8.11M | fn get_transaction_count(&self) -> usize { |
117 | 8.11M | self.transactions.len() |
118 | 8.11M | } |
119 | | |
120 | 15.6M | fn get_transaction_by_index(&self, index: usize) -> Option<&LdapTransaction> { |
121 | 15.6M | self.transactions.get(index) |
122 | 15.6M | } |
123 | | } |
124 | | |
125 | | impl LdapState { |
126 | 4.61k | pub fn new() -> Self { |
127 | 4.61k | Self { |
128 | 4.61k | state_data: AppLayerStateData::new(), |
129 | 4.61k | tx_id: 0, |
130 | 4.61k | transactions: VecDeque::new(), |
131 | 4.61k | request_frame: None, |
132 | 4.61k | response_frame: None, |
133 | 4.61k | request_gap: false, |
134 | 4.61k | response_gap: false, |
135 | 4.61k | request_tls: false, |
136 | 4.61k | has_starttls: false, |
137 | 4.61k | } |
138 | 4.61k | } |
139 | | |
140 | | // Free a transaction by ID. |
141 | 321k | fn free_tx(&mut self, tx_id: u64) { |
142 | 321k | let len = self.transactions.len(); |
143 | 321k | let mut found = false; |
144 | 321k | let mut index = 0; |
145 | 2.27M | for i in 0..len { |
146 | 2.27M | let tx = &self.transactions[i]; |
147 | 2.27M | if tx.tx_id == tx_id + 1 { |
148 | 321k | found = true; |
149 | 321k | index = i; |
150 | 321k | break; |
151 | 1.95M | } |
152 | | } |
153 | 321k | if found { |
154 | 321k | self.transactions.remove(index); |
155 | 321k | } |
156 | 321k | } |
157 | | |
158 | 0 | pub fn get_tx(&mut self, tx_id: u64) -> Option<&LdapTransaction> { |
159 | 0 | self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1) |
160 | 0 | } |
161 | | |
162 | 368k | pub fn new_tx(&mut self) -> Option<LdapTransaction> { |
163 | 368k | if self.transactions.len() > unsafe { LDAP_MAX_TX } { |
164 | 20.6k | for tx_old in &mut self.transactions { |
165 | 20.5k | if !tx_old.complete { |
166 | 16.2k | tx_old.tx_data.updated_tc = true; |
167 | 16.2k | tx_old.tx_data.updated_ts = true; |
168 | 16.2k | tx_old.complete = true; |
169 | 16.2k | tx_old |
170 | 16.2k | .tx_data |
171 | 16.2k | .set_event(LdapEvent::TooManyTransactions as u8); |
172 | 16.2k | } |
173 | | } |
174 | 80 | return None; |
175 | 368k | } |
176 | 368k | let mut tx = LdapTransaction::new(); |
177 | 368k | self.tx_id += 1; |
178 | 368k | tx.tx_id = self.tx_id; |
179 | 368k | return Some(tx); |
180 | 368k | } |
181 | | |
182 | 175k | fn set_event(&mut self, e: LdapEvent) { |
183 | 175k | if let Some(tx) = self.transactions.back_mut() { |
184 | 174k | tx.tx_data.set_event(e as u8); |
185 | 174k | } |
186 | 175k | } |
187 | | |
188 | 249k | fn find_request(&mut self, message_id: MessageID) -> Option<&mut LdapTransaction> { |
189 | 3.23M | self.transactions.iter_mut().find(|tx| { |
190 | 3.23M | tx.request |
191 | 3.23M | .as_ref() |
192 | 3.23M | .is_some_and(|req| req.message_id == message_id) |
193 | 3.23M | }) |
194 | 249k | } |
195 | | |
196 | 239k | fn parse_request(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult { |
197 | 239k | let input = stream_slice.as_slice(); |
198 | 239k | if input.is_empty() { |
199 | 0 | return AppLayerResult::ok(); |
200 | 239k | } |
201 | | |
202 | 239k | if self.has_starttls { |
203 | 0 | unsafe { |
204 | 0 | SCAppLayerRequestProtocolTLSUpgrade(flow); |
205 | 0 | } |
206 | 0 | return AppLayerResult::ok(); |
207 | 239k | } |
208 | | |
209 | 239k | if self.request_gap { |
210 | 0 | if ldap_parse_msg(input).is_err() { |
211 | 0 | return AppLayerResult::ok(); |
212 | 0 | } |
213 | 0 | self.request_gap = false; |
214 | 239k | } |
215 | | |
216 | 239k | let mut start = input; |
217 | 416k | while !start.is_empty() { |
218 | 331k | if self.request_frame.is_none() { |
219 | 331k | self.request_frame = Frame::new( |
220 | 331k | flow, |
221 | 331k | &stream_slice, |
222 | 331k | start, |
223 | 331k | -1_i64, |
224 | 331k | LdapFrameType::Pdu as u8, |
225 | 331k | None, |
226 | 331k | ); |
227 | 331k | SCLogDebug!("ts: pdu {:?}", self.request_frame); |
228 | 331k | } |
229 | 331k | match ldap_parse_msg(start) { |
230 | 176k | Ok((rem, request)) => { |
231 | 176k | let tx = self.new_tx(); |
232 | 176k | if tx.is_none() { |
233 | 39 | return AppLayerResult::err(); |
234 | 176k | } |
235 | 176k | let mut tx = tx.unwrap(); |
236 | 176k | let tx_id = tx.id(); |
237 | | // check if STARTTLS was requested |
238 | 176k | if let ProtocolOp::ExtendedRequest(request) = &request.protocol_op { |
239 | 1.33k | if request.request_name.0 == STARTTLS_OID { |
240 | 0 | self.request_tls = true; |
241 | 1.33k | } |
242 | 175k | } |
243 | 176k | tx.complete |= tx_is_complete(&request.protocol_op, Direction::ToServer); |
244 | 176k | tx.request = Some(request.to_static()); |
245 | 176k | self.transactions.push_back(tx); |
246 | 176k | sc_app_layer_parser_trigger_raw_stream_inspection( |
247 | 176k | flow, |
248 | 176k | Direction::ToServer as i32, |
249 | | ); |
250 | 176k | let consumed = start.len() - rem.len(); |
251 | 176k | start = rem; |
252 | 176k | self.set_frame_ts(flow, tx_id, consumed as i64); |
253 | | } |
254 | | Err(nom::Err::Incomplete(_)) => { |
255 | 153k | let consumed = input.len() - start.len(); |
256 | 153k | let needed = start.len() + 1; |
257 | 153k | return AppLayerResult::incomplete(consumed as u32, needed as u32); |
258 | | } |
259 | | Err(_) => { |
260 | 845 | self.set_event(LdapEvent::InvalidData); |
261 | 845 | return AppLayerResult::err(); |
262 | | } |
263 | | } |
264 | | } |
265 | | |
266 | 85.2k | return AppLayerResult::ok(); |
267 | 239k | } |
268 | | |
269 | 240k | fn parse_response(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult { |
270 | 240k | let input = stream_slice.as_slice(); |
271 | 240k | if input.is_empty() { |
272 | 0 | return AppLayerResult::ok(); |
273 | 240k | } |
274 | | |
275 | 240k | if self.response_gap { |
276 | 0 | if ldap_parse_msg(input).is_err() { |
277 | 0 | return AppLayerResult::ok(); |
278 | 0 | } |
279 | 0 | self.response_gap = false; |
280 | 240k | } |
281 | | |
282 | 240k | let mut start = input; |
283 | 473k | while !start.is_empty() { |
284 | 388k | if self.response_frame.is_none() { |
285 | 388k | self.response_frame = Frame::new( |
286 | 388k | flow, |
287 | 388k | &stream_slice, |
288 | 388k | start, |
289 | 388k | -1_i64, |
290 | 388k | LdapFrameType::Pdu as u8, |
291 | 388k | None, |
292 | 388k | ); |
293 | 388k | SCLogDebug!("tc: pdu {:?}", self.response_frame); |
294 | 388k | } |
295 | 388k | match ldap_parse_msg(start) { |
296 | 232k | Ok((rem, response)) => { |
297 | | // check if STARTTLS was requested |
298 | 232k | if self.request_tls { |
299 | 0 | if let ProtocolOp::ExtendedResponse(response) = &response.protocol_op { |
300 | 0 | if response.result.result_code == ResultCode(0) { |
301 | 0 | SCLogDebug!("LDAP: STARTTLS detected"); |
302 | 0 | self.has_starttls = true; |
303 | 0 | } |
304 | 0 | self.request_tls = false; |
305 | 0 | } |
306 | 232k | } |
307 | 232k | if let Some(tx) = self.find_request(response.message_id) { |
308 | 61.2k | tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); |
309 | 61.2k | let tx_id = tx.id(); |
310 | 61.2k | tx.tx_data.updated_tc = true; |
311 | 61.2k | if tx.responses.len() < unsafe { LDAP_MAX_RESPONSES } { |
312 | 55.7k | tx.responses.push_back(response.to_static()); |
313 | 55.7k | } else { |
314 | 5.52k | tx.set_event(LdapEvent::TooManyResponses); |
315 | 5.52k | } |
316 | 61.2k | sc_app_layer_parser_trigger_raw_stream_inspection( |
317 | 61.2k | flow, |
318 | 61.2k | Direction::ToClient as i32, |
319 | | ); |
320 | 61.2k | let consumed = start.len() - rem.len(); |
321 | 61.2k | self.set_frame_tc(flow, tx_id, consumed as i64); |
322 | 171k | } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { |
323 | | // this is an unsolicited notification, which means |
324 | | // there is no request |
325 | 605 | let tx = self.new_tx(); |
326 | 605 | if tx.is_none() { |
327 | 2 | return AppLayerResult::err(); |
328 | 603 | } |
329 | 603 | let mut tx = tx.unwrap(); |
330 | 603 | let tx_id = tx.id(); |
331 | 603 | tx.complete = true; |
332 | 603 | tx.responses.push_back(response.to_static()); |
333 | 603 | self.transactions.push_back(tx); |
334 | 603 | sc_app_layer_parser_trigger_raw_stream_inspection( |
335 | 603 | flow, |
336 | 603 | Direction::ToClient as i32, |
337 | | ); |
338 | 603 | let consumed = start.len() - rem.len(); |
339 | 603 | self.set_frame_tc(flow, tx_id, consumed as i64); |
340 | | } else { |
341 | 170k | let tx = self.new_tx(); |
342 | 170k | if tx.is_none() { |
343 | 34 | return AppLayerResult::err(); |
344 | 170k | } |
345 | 170k | let mut tx = tx.unwrap(); |
346 | 170k | tx.complete = true; |
347 | 170k | let tx_id = tx.id(); |
348 | 170k | tx.responses.push_back(response.to_static()); |
349 | 170k | self.transactions.push_back(tx); |
350 | 170k | sc_app_layer_parser_trigger_raw_stream_inspection( |
351 | 170k | flow, |
352 | 170k | Direction::ToClient as i32, |
353 | | ); |
354 | 170k | self.set_event(LdapEvent::RequestNotFound); |
355 | 170k | let consumed = start.len() - rem.len(); |
356 | 170k | self.set_frame_tc(flow, tx_id, consumed as i64); |
357 | | }; |
358 | 232k | start = rem; |
359 | | } |
360 | | Err(nom::Err::Incomplete(_)) => { |
361 | 155k | let consumed = input.len() - start.len(); |
362 | 155k | let needed = start.len() + 1; |
363 | 155k | return AppLayerResult::incomplete(consumed as u32, needed as u32); |
364 | | } |
365 | | Err(_) => { |
366 | 456 | self.set_event(LdapEvent::InvalidData); |
367 | 456 | return AppLayerResult::err(); |
368 | | } |
369 | | } |
370 | | } |
371 | | |
372 | 84.3k | return AppLayerResult::ok(); |
373 | 240k | } |
374 | | |
375 | 15.1k | fn parse_request_udp( |
376 | 15.1k | &mut self, flow: *const Flow, stream_slice: StreamSlice, |
377 | 15.1k | ) -> AppLayerResult { |
378 | 15.1k | let input = stream_slice.as_slice(); |
379 | | |
380 | 15.1k | let _pdu = Frame::new( |
381 | 15.1k | flow, |
382 | 15.1k | &stream_slice, |
383 | 15.1k | input, |
384 | 15.1k | input.len() as i64, |
385 | 15.1k | LdapFrameType::Pdu as u8, |
386 | 15.1k | None, |
387 | | ); |
388 | | SCLogDebug!("ts: pdu {:?}", self.request_frame); |
389 | | |
390 | 15.1k | match ldap_parse_msg(input) { |
391 | 14.8k | Ok((_, request)) => { |
392 | 14.8k | let tx = self.new_tx(); |
393 | 14.8k | if tx.is_none() { |
394 | 2 | return AppLayerResult::err(); |
395 | 14.8k | } |
396 | 14.8k | let mut tx = tx.unwrap(); |
397 | 14.8k | tx.complete |= tx_is_complete(&request.protocol_op, Direction::ToServer); |
398 | 14.8k | tx.request = Some(request.to_static()); |
399 | 14.8k | self.transactions.push_back(tx); |
400 | | } |
401 | | Err(nom::Err::Incomplete(_)) => { |
402 | 173 | self.set_event(LdapEvent::IncompleteData); |
403 | 173 | return AppLayerResult::err(); |
404 | | } |
405 | | Err(_) => { |
406 | 126 | self.set_event(LdapEvent::InvalidData); |
407 | 126 | return AppLayerResult::err(); |
408 | | } |
409 | | } |
410 | | |
411 | 14.8k | return AppLayerResult::ok(); |
412 | 15.1k | } |
413 | | |
414 | 14.5k | fn parse_response_udp( |
415 | 14.5k | &mut self, flow: *const Flow, stream_slice: StreamSlice, |
416 | 14.5k | ) -> AppLayerResult { |
417 | 14.5k | let input = stream_slice.as_slice(); |
418 | 14.5k | if input.is_empty() { |
419 | 0 | return AppLayerResult::ok(); |
420 | 14.5k | } |
421 | | |
422 | 14.5k | let mut start = input; |
423 | 31.5k | while !start.is_empty() { |
424 | 17.2k | if self.response_frame.is_none() { |
425 | 17.2k | self.response_frame = Frame::new( |
426 | 17.2k | flow, |
427 | 17.2k | &stream_slice, |
428 | 17.2k | start, |
429 | 17.2k | -1_i64, |
430 | 17.2k | LdapFrameType::Pdu as u8, |
431 | 17.2k | None, |
432 | 17.2k | ); |
433 | 17.2k | SCLogDebug!("tc: pdu {:?}", self.response_frame); |
434 | 17.2k | } |
435 | 17.2k | match ldap_parse_msg(start) { |
436 | 17.0k | Ok((rem, response)) => { |
437 | 17.0k | if let Some(tx) = self.find_request(response.message_id) { |
438 | 11.9k | tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); |
439 | 11.9k | let tx_id = tx.id(); |
440 | 11.9k | if tx.responses.len() < unsafe { LDAP_MAX_RESPONSES } { |
441 | 11.6k | tx.responses.push_back(response.to_static()); |
442 | 11.6k | } else { |
443 | 237 | tx.set_event(LdapEvent::TooManyResponses); |
444 | 237 | } |
445 | 11.9k | let consumed = start.len() - rem.len(); |
446 | 11.9k | self.set_frame_tc(flow, tx_id, consumed as i64); |
447 | 5.10k | } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { |
448 | | // this is an unsolicited notification, which means |
449 | | // there is no request |
450 | 1.84k | let tx = self.new_tx(); |
451 | 1.84k | if tx.is_none() { |
452 | 1 | return AppLayerResult::err(); |
453 | 1.84k | } |
454 | 1.84k | let mut tx = tx.unwrap(); |
455 | 1.84k | tx.complete = true; |
456 | 1.84k | let tx_id = tx.id(); |
457 | 1.84k | tx.responses.push_back(response.to_static()); |
458 | 1.84k | self.transactions.push_back(tx); |
459 | 1.84k | let consumed = start.len() - rem.len(); |
460 | 1.84k | self.set_frame_tc(flow, tx_id, consumed as i64); |
461 | | } else { |
462 | 3.25k | let tx = self.new_tx(); |
463 | 3.25k | if tx.is_none() { |
464 | 2 | return AppLayerResult::err(); |
465 | 3.25k | } |
466 | 3.25k | let mut tx = tx.unwrap(); |
467 | 3.25k | tx.complete = true; |
468 | 3.25k | let tx_id = tx.id(); |
469 | 3.25k | tx.responses.push_back(response.to_static()); |
470 | 3.25k | self.transactions.push_back(tx); |
471 | 3.25k | self.set_event(LdapEvent::RequestNotFound); |
472 | 3.25k | let consumed = start.len() - rem.len(); |
473 | 3.25k | self.set_frame_tc(flow, tx_id, consumed as i64); |
474 | | }; |
475 | 17.0k | start = rem; |
476 | | } |
477 | | Err(nom::Err::Incomplete(_)) => { |
478 | 121 | self.set_event(LdapEvent::IncompleteData); |
479 | 121 | return AppLayerResult::err(); |
480 | | } |
481 | | Err(_) => { |
482 | 67 | self.set_event(LdapEvent::InvalidData); |
483 | 67 | return AppLayerResult::err(); |
484 | | } |
485 | | } |
486 | | } |
487 | | |
488 | 14.3k | return AppLayerResult::ok(); |
489 | 14.5k | } |
490 | | |
491 | 176k | fn set_frame_ts(&mut self, flow: *const Flow, tx_id: u64, consumed: i64) { |
492 | 176k | if let Some(frame) = &self.request_frame { |
493 | 0 | frame.set_len(flow, consumed); |
494 | 0 | frame.set_tx(flow, tx_id); |
495 | 0 | self.request_frame = None; |
496 | 176k | } |
497 | 176k | } |
498 | | |
499 | 249k | fn set_frame_tc(&mut self, flow: *const Flow, tx_id: u64, consumed: i64) { |
500 | 249k | if let Some(frame) = &self.response_frame { |
501 | 0 | frame.set_len(flow, consumed); |
502 | 0 | frame.set_tx(flow, tx_id); |
503 | 0 | self.response_frame = None; |
504 | 249k | } |
505 | 249k | } |
506 | | |
507 | 0 | fn on_request_gap(&mut self, _size: u32) { |
508 | 0 | self.request_gap = true; |
509 | 0 | } |
510 | | |
511 | 0 | fn on_response_gap(&mut self, _size: u32) { |
512 | 0 | self.response_gap = true; |
513 | 0 | } |
514 | | } |
515 | | |
516 | 264k | fn tx_is_complete(op: &ProtocolOp, dir: Direction) -> bool { |
517 | 264k | match dir { |
518 | 191k | Direction::ToServer => match op { |
519 | 377 | ProtocolOp::UnbindRequest => true, |
520 | 148k | ProtocolOp::AbandonRequest(_) => true, |
521 | 43.0k | _ => false, |
522 | | }, |
523 | 73.1k | Direction::ToClient => match op { |
524 | | ProtocolOp::SearchResultDone(_) |
525 | | | ProtocolOp::BindResponse(_) |
526 | | | ProtocolOp::ModifyResponse(_) |
527 | | | ProtocolOp::AddResponse(_) |
528 | | | ProtocolOp::DelResponse(_) |
529 | | | ProtocolOp::ModDnResponse(_) |
530 | | | ProtocolOp::CompareResponse(_) |
531 | 3.80k | | ProtocolOp::ExtendedResponse(_) => true, |
532 | 69.3k | _ => false, |
533 | | }, |
534 | | } |
535 | 264k | } |
536 | | |
537 | 1.20k | fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> AppProto { |
538 | 1.20k | match ldap_parse_msg(input) { |
539 | 140 | Ok((_, ldap_msg)) => { |
540 | 140 | if direction == Direction::ToServer && !ldap_is_request(&ldap_msg) { |
541 | 57 | unsafe { |
542 | 57 | *rdir = Direction::ToClient.into(); |
543 | 57 | } |
544 | 83 | } |
545 | 140 | if direction == Direction::ToClient && !ldap_is_response(&ldap_msg) { |
546 | 19 | unsafe { |
547 | 19 | *rdir = Direction::ToServer.into(); |
548 | 19 | } |
549 | 121 | } |
550 | 140 | return unsafe { ALPROTO_LDAP }; |
551 | | } |
552 | | Err(nom::Err::Incomplete(_)) => { |
553 | 381 | return ALPROTO_UNKNOWN; |
554 | | } |
555 | 688 | Err(_e) => { |
556 | 688 | return ALPROTO_FAILED; |
557 | | } |
558 | | } |
559 | 1.20k | } |
560 | | |
561 | 1.21k | unsafe extern "C" fn ldap_probing_parser( |
562 | 1.21k | _flow: *const Flow, direction: u8, input: *const u8, input_len: u32, rdir: *mut u8, |
563 | 1.21k | ) -> AppProto { |
564 | 1.21k | if input_len > 1 && !input.is_null() { |
565 | 1.20k | let slice = build_slice!(input, input_len as usize); |
566 | 1.20k | return probe(slice, direction.into(), rdir); |
567 | 1 | } |
568 | 1 | return ALPROTO_UNKNOWN; |
569 | 1.21k | } |
570 | | |
571 | 4.61k | extern "C" fn ldap_state_new(_orig_state: *mut c_void, _orig_proto: AppProto) -> *mut c_void { |
572 | 4.61k | let state = LdapState::new(); |
573 | 4.61k | let boxed = Box::new(state); |
574 | 4.61k | return Box::into_raw(boxed) as *mut c_void; |
575 | 4.61k | } |
576 | | |
577 | 4.61k | unsafe extern "C" fn ldap_state_free(state: *mut c_void) { |
578 | 4.61k | std::mem::drop(Box::from_raw(state as *mut LdapState)); |
579 | 4.61k | } |
580 | | |
581 | 321k | unsafe extern "C" fn ldap_state_tx_free(state: *mut c_void, tx_id: u64) { |
582 | 321k | let state = cast_pointer!(state, LdapState); |
583 | 321k | state.free_tx(tx_id); |
584 | 321k | } |
585 | | |
586 | 239k | unsafe extern "C" fn ldap_parse_request( |
587 | 239k | flow: *mut Flow, state: *mut c_void, pstate: *mut AppLayerParserState, |
588 | 239k | stream_slice: StreamSlice, _data: *const c_void, |
589 | 239k | ) -> AppLayerResult { |
590 | 239k | if stream_slice.is_empty() { |
591 | 0 | if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 { |
592 | 0 | return AppLayerResult::ok(); |
593 | | } else { |
594 | 0 | return AppLayerResult::err(); |
595 | | } |
596 | 239k | } |
597 | 239k | let state = cast_pointer!(state, LdapState); |
598 | | |
599 | 239k | if stream_slice.is_gap() { |
600 | 0 | state.on_request_gap(stream_slice.gap_size()); |
601 | 0 | } else { |
602 | 239k | return state.parse_request(flow, stream_slice); |
603 | | } |
604 | 0 | AppLayerResult::ok() |
605 | 239k | } |
606 | | |
607 | 240k | unsafe extern "C" fn ldap_parse_response( |
608 | 240k | flow: *mut Flow, state: *mut c_void, pstate: *mut AppLayerParserState, |
609 | 240k | stream_slice: StreamSlice, _data: *const c_void, |
610 | 240k | ) -> AppLayerResult { |
611 | 240k | if stream_slice.is_empty() { |
612 | 0 | if SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 { |
613 | 0 | return AppLayerResult::ok(); |
614 | | } else { |
615 | 0 | return AppLayerResult::err(); |
616 | | } |
617 | 240k | } |
618 | 240k | let state = cast_pointer!(state, LdapState); |
619 | 240k | if stream_slice.is_gap() { |
620 | 0 | state.on_response_gap(stream_slice.gap_size()); |
621 | 0 | } else { |
622 | 240k | return state.parse_response(flow, stream_slice); |
623 | | } |
624 | 0 | AppLayerResult::ok() |
625 | 240k | } |
626 | | |
627 | 15.1k | unsafe extern "C" fn ldap_parse_request_udp( |
628 | 15.1k | flow: *mut Flow, state: *mut c_void, _pstate: *mut AppLayerParserState, |
629 | 15.1k | stream_slice: StreamSlice, _data: *const c_void, |
630 | 15.1k | ) -> AppLayerResult { |
631 | 15.1k | let state = cast_pointer!(state, LdapState); |
632 | 15.1k | state.parse_request_udp(flow, stream_slice) |
633 | 15.1k | } |
634 | | |
635 | 14.5k | unsafe extern "C" fn ldap_parse_response_udp( |
636 | 14.5k | flow: *mut Flow, state: *mut c_void, _pstate: *mut AppLayerParserState, |
637 | 14.5k | stream_slice: StreamSlice, _data: *const c_void, |
638 | 14.5k | ) -> AppLayerResult { |
639 | 14.5k | let state = cast_pointer!(state, LdapState); |
640 | 14.5k | state.parse_response_udp(flow, stream_slice) |
641 | 14.5k | } |
642 | | |
643 | 0 | unsafe extern "C" fn ldap_state_get_tx(state: *mut c_void, tx_id: u64) -> *mut c_void { |
644 | 0 | let state = cast_pointer!(state, LdapState); |
645 | 0 | match state.get_tx(tx_id) { |
646 | 0 | Some(tx) => { |
647 | 0 | return tx as *const _ as *mut _; |
648 | | } |
649 | | None => { |
650 | 0 | return std::ptr::null_mut(); |
651 | | } |
652 | | } |
653 | 0 | } |
654 | | |
655 | 1.52M | unsafe extern "C" fn ldap_state_get_tx_count(state: *mut c_void) -> u64 { |
656 | 1.52M | let state = cast_pointer!(state, LdapState); |
657 | 1.52M | return state.tx_id; |
658 | 1.52M | } |
659 | | |
660 | 8.35M | unsafe extern "C" fn ldap_tx_get_alstate_progress(tx: *mut c_void, _direction: u8) -> c_int { |
661 | 8.35M | let tx = cast_pointer!(tx, LdapTransaction); |
662 | 8.35M | if tx.complete { |
663 | 643k | return 1; |
664 | 7.71M | } |
665 | 7.71M | return 0; |
666 | 8.35M | } |
667 | | |
668 | | export_tx_data_get!(ldap_get_tx_data, LdapTransaction); |
669 | | export_state_data_get!(ldap_get_state_data, LdapState); |
670 | | |
671 | | const PARSER_NAME: &[u8] = b"ldap\0"; |
672 | | |
673 | | #[no_mangle] |
674 | 40 | pub unsafe extern "C" fn SCRegisterLdapTcpParser() { |
675 | 40 | let default_port = CString::new("[389, 3268]").unwrap(); |
676 | 40 | let parser = RustParser { |
677 | 40 | name: PARSER_NAME.as_ptr() as *const c_char, |
678 | 40 | default_port: default_port.as_ptr(), |
679 | 40 | ipproto: IPPROTO_TCP, |
680 | 40 | probe_ts: Some(ldap_probing_parser), |
681 | 40 | probe_tc: Some(ldap_probing_parser), |
682 | 40 | min_depth: 0, |
683 | 40 | max_depth: 16, |
684 | 40 | state_new: ldap_state_new, |
685 | 40 | state_free: ldap_state_free, |
686 | 40 | tx_free: ldap_state_tx_free, |
687 | 40 | parse_ts: ldap_parse_request, |
688 | 40 | parse_tc: ldap_parse_response, |
689 | 40 | get_tx_count: ldap_state_get_tx_count, |
690 | 40 | get_tx: ldap_state_get_tx, |
691 | 40 | tx_comp_st_ts: 1, |
692 | 40 | tx_comp_st_tc: 1, |
693 | 40 | tx_get_progress: ldap_tx_get_alstate_progress, |
694 | 40 | get_eventinfo: Some(LdapEvent::get_event_info), |
695 | 40 | get_eventinfo_byid: Some(LdapEvent::get_event_info_by_id), |
696 | 40 | localstorage_new: None, |
697 | 40 | localstorage_free: None, |
698 | 40 | get_tx_files: None, |
699 | 40 | get_tx_iterator: Some(applayer::state_get_tx_iterator::<LdapState, LdapTransaction>), |
700 | 40 | get_tx_data: ldap_get_tx_data, |
701 | 40 | get_state_data: ldap_get_state_data, |
702 | 40 | apply_tx_config: None, |
703 | 40 | flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, |
704 | 40 | get_frame_id_by_name: Some(LdapFrameType::ffi_id_from_name), |
705 | 40 | get_frame_name_by_id: Some(LdapFrameType::ffi_name_from_id), |
706 | 40 | get_state_id_by_name: None, |
707 | 40 | get_state_name_by_id: None, |
708 | 40 | }; |
709 | | |
710 | 40 | let ip_proto_str = CString::new("tcp").unwrap(); |
711 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
712 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
713 | 40 | ALPROTO_LDAP = alproto; |
714 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
715 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
716 | 40 | } |
717 | 40 | if let Some(val) = conf_get("app-layer.protocols.ldap.max-tx") { |
718 | 0 | if let Ok(v) = val.parse::<usize>() { |
719 | 0 | if LDAP_MAX_TX == LDAP_MAX_TX_DEFAULT { |
720 | 0 | LDAP_MAX_TX = v; |
721 | 0 | } |
722 | | } else { |
723 | 0 | SCLogError!("Invalid value for ldap.max-tx"); |
724 | | } |
725 | 40 | } |
726 | 40 | if let Some(val) = conf_get("app-layer.protocols.ldap.max-responses") { |
727 | 0 | if let Ok(v) = val.parse::<usize>() { |
728 | 0 | LDAP_MAX_RESPONSES = v; |
729 | 0 | } else { |
730 | 0 | SCLogWarning!("Invalid value for ldap.max-responses"); |
731 | | } |
732 | 40 | } |
733 | 40 | SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_LDAP); |
734 | 0 | } else { |
735 | 0 | SCLogDebug!("Protocol detection and parser disabled for LDAP/TCP."); |
736 | 0 | } |
737 | 40 | } |
738 | | |
739 | | #[no_mangle] |
740 | 40 | pub unsafe extern "C" fn SCRegisterLdapUdpParser() { |
741 | 40 | let default_port = CString::new("[389, 3268]").unwrap(); |
742 | 40 | let parser = RustParser { |
743 | 40 | name: PARSER_NAME.as_ptr() as *const c_char, |
744 | 40 | default_port: default_port.as_ptr(), |
745 | 40 | ipproto: IPPROTO_UDP, |
746 | 40 | probe_ts: Some(ldap_probing_parser), |
747 | 40 | probe_tc: Some(ldap_probing_parser), |
748 | 40 | min_depth: 0, |
749 | 40 | max_depth: 16, |
750 | 40 | state_new: ldap_state_new, |
751 | 40 | state_free: ldap_state_free, |
752 | 40 | tx_free: ldap_state_tx_free, |
753 | 40 | parse_ts: ldap_parse_request_udp, |
754 | 40 | parse_tc: ldap_parse_response_udp, |
755 | 40 | get_tx_count: ldap_state_get_tx_count, |
756 | 40 | get_tx: ldap_state_get_tx, |
757 | 40 | tx_comp_st_ts: 1, |
758 | 40 | tx_comp_st_tc: 1, |
759 | 40 | tx_get_progress: ldap_tx_get_alstate_progress, |
760 | 40 | get_eventinfo: Some(LdapEvent::get_event_info), |
761 | 40 | get_eventinfo_byid: Some(LdapEvent::get_event_info_by_id), |
762 | 40 | localstorage_new: None, |
763 | 40 | localstorage_free: None, |
764 | 40 | get_tx_files: None, |
765 | 40 | get_tx_iterator: Some(applayer::state_get_tx_iterator::<LdapState, LdapTransaction>), |
766 | 40 | get_tx_data: ldap_get_tx_data, |
767 | 40 | get_state_data: ldap_get_state_data, |
768 | 40 | apply_tx_config: None, |
769 | 40 | flags: 0, |
770 | 40 | get_frame_id_by_name: Some(LdapFrameType::ffi_id_from_name), |
771 | 40 | get_frame_name_by_id: Some(LdapFrameType::ffi_name_from_id), |
772 | 40 | get_state_id_by_name: None, |
773 | 40 | get_state_name_by_id: None, |
774 | 40 | }; |
775 | | |
776 | 40 | let ip_proto_str = CString::new("udp").unwrap(); |
777 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
778 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
779 | 40 | ALPROTO_LDAP = alproto; |
780 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
781 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
782 | 40 | } |
783 | 40 | if let Some(val) = conf_get("app-layer.protocols.ldap.max-tx") { |
784 | 0 | if let Ok(v) = val.parse::<usize>() { |
785 | 0 | if LDAP_MAX_TX == LDAP_MAX_TX_DEFAULT { |
786 | 0 | LDAP_MAX_TX = v; |
787 | 0 | } |
788 | | } else { |
789 | 0 | SCLogError!("Invalid value for ldap.max-tx"); |
790 | | } |
791 | 40 | } |
792 | 40 | if let Some(val) = conf_get("app-layer.protocols.ldap.max-responses") { |
793 | 0 | if let Ok(v) = val.parse::<usize>() { |
794 | 0 | LDAP_MAX_RESPONSES = v; |
795 | 0 | } else { |
796 | 0 | SCLogWarning!("Invalid value for ldap.max-responses"); |
797 | | } |
798 | 40 | } |
799 | 40 | SCAppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_LDAP); |
800 | 0 | } else { |
801 | 0 | SCLogDebug!("Protocol detection and parser disabled for LDAP/UDP."); |
802 | 0 | } |
803 | 40 | } |