diff --git a/etc/schema.json b/etc/schema.json index cffb15afd357..1a4c24787f9d 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -4637,8 +4637,12 @@ "Errors encountered parsing Kerberos v5/UDP protocol", "$ref": "#/$defs/stats_applayer_error" }, - "ldap": { - "description": "Errors encountered parsing LDAP protocol", + "ldap_tcp": { + "description": "Errors encountered parsing LDAP/TCP protocol", + "$ref": "#/$defs/stats_applayer_error" + }, + "ldap_udp": { + "description": "Errors encountered parsing LDAP/UDP protocol", "$ref": "#/$defs/stats_applayer_error" }, "modbus": { @@ -4805,8 +4809,12 @@ "description": "Number of flows for Kerberos v5/UDP protocol", "type": "integer" }, - "ldap": { - "description": "Number of flows for LDAP protocol", + "ldap_tcp": { + "description": "Number of flows for LDAP/TCP protocol", + "type": "integer" + }, + "ldap_udp": { + "description": "Errors encountered parsing LDAP/UDP protocol", "type": "integer" }, "modbus": { @@ -4968,8 +4976,12 @@ "Number of transactions for Kerberos v5/UDP protocol", "type": "integer" }, - "ldap": { - "description": "Number of transactions for LDAP protocol", + "ldap_tcp": { + "description": "Number of transactions for LDAP/TCP protocol", + "type": "integer" + }, + "ldap_udp": { + "description": "Errors encountered parsing LDAP/UDP protocol", "type": "integer" }, "modbus": { diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index 0817b4c9fba5..1161c6f9882d 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -19,7 +19,8 @@ use crate::applayer::{self, *}; use crate::conf::conf_get; -use crate::core::*; +use crate::core::{Flow, *}; +use crate::frames::*; use nom7 as nom; use std; use std::collections::VecDeque; @@ -32,6 +33,11 @@ static mut LDAP_MAX_TX: usize = 256; static mut ALPROTO_LDAP: AppProto = ALPROTO_UNKNOWN; +#[derive(AppLayerFrameType)] +pub enum LdapFrameType { + Pdu, +} + #[derive(AppLayerEvent)] enum LdapEvent { TooManyTransactions, @@ -79,6 +85,8 @@ pub struct LdapState { tx_id: u64, transactions: VecDeque, tx_index_completed: usize, + request_frame: Option, + response_frame: Option, } impl State for LdapState { @@ -98,6 +106,8 @@ impl LdapState { tx_id: 0, transactions: VecDeque::new(), tx_index_completed: 0, + request_frame: None, + response_frame: None, } } @@ -158,17 +168,29 @@ impl LdapState { }) } - fn parse_request(&mut self, input: &[u8]) -> AppLayerResult { + fn parse_request(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); if input.is_empty() { return AppLayerResult::ok(); } let mut start = input; while !start.is_empty() { + if self.request_frame.is_none() { + self.request_frame = Frame::new( + flow, + &stream_slice, + start, + -1_i64, + LdapFrameType::Pdu as u8, + None, + ); + SCLogDebug!("ts: pdu {:?}", self.request_frame); + } match ldap_parse_msg(start) { Ok((rem, msg)) => { - start = rem; let mut tx = self.new_tx(); + let tx_id = tx.id(); let request = LdapMessage::from(msg); tx.complete = match request.protocol_op { ProtocolOp::UnbindRequest => true, @@ -176,6 +198,14 @@ impl LdapState { }; tx.request = Some(request); self.transactions.push_back(tx); + + let consumed = start.len() - rem.len(); + start = rem; + if let Some(frame) = &self.request_frame { + frame.set_len(flow, consumed as i64); + frame.set_tx(flow, tx_id); + self.request_frame = None; + } } Err(nom::Err::Incomplete(_)) => { let consumed = input.len() - start.len(); @@ -191,16 +221,27 @@ impl LdapState { return AppLayerResult::ok(); } - fn parse_response(&mut self, input: &[u8]) -> AppLayerResult { + fn parse_response(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); if input.is_empty() { return AppLayerResult::ok(); } + let mut start = input; while !start.is_empty() { + if self.response_frame.is_none() { + self.response_frame = Frame::new( + flow, + &stream_slice, + start, + -1_i64, + LdapFrameType::Pdu as u8, + None, + ); + SCLogDebug!("tc: pdu {:?}", self.response_frame); + } match ldap_parse_msg(start) { Ok((rem, msg)) => { - start = rem; - let response = LdapMessage::from(msg); if let Some(tx) = self.find_request(response.message_id) { tx.complete = match response.protocol_op { @@ -214,21 +255,43 @@ impl LdapState { | ProtocolOp::ExtendedResponse(_) => true, _ => false, }; + let tx_id = tx.id(); tx.responses.push_back(response); + let consumed = start.len() - rem.len(); + if let Some(frame) = &self.response_frame { + frame.set_len(flow, consumed as i64); + frame.set_tx(flow, tx_id); + self.response_frame = None; + } } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { // this is an unsolicited notification, which means // there is no request let mut tx = self.new_tx(); + let tx_id = tx.id(); tx.complete = true; tx.responses.push_back(response); self.transactions.push_back(tx); + let consumed = start.len() - rem.len(); + if let Some(frame) = &self.response_frame { + frame.set_len(flow, consumed as i64); + frame.set_tx(flow, tx_id); + self.response_frame = None; + } } else { let mut tx = self.new_tx(); tx.complete = true; + let tx_id = tx.id(); tx.responses.push_back(response); self.transactions.push_back(tx); self.set_event(LdapEvent::RequestNotFound); + let consumed = start.len() - rem.len(); + if let Some(frame) = &self.response_frame { + frame.set_len(flow, consumed as i64); + frame.set_tx(flow, tx_id); + self.response_frame = None; + } }; + start = rem; } Err(nom::Err::Incomplete(_)) => { let consumed = input.len() - start.len(); @@ -305,7 +368,7 @@ unsafe extern "C" fn SCLdapStateTxFree(state: *mut c_void, tx_id: u64) { #[no_mangle] unsafe extern "C" fn SCLdapParseRequest( - _flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, + flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, _data: *const c_void, ) -> AppLayerResult { if stream_slice.is_empty() { @@ -316,14 +379,12 @@ unsafe extern "C" fn SCLdapParseRequest( } } let state = cast_pointer!(state, LdapState); - state.parse_request(stream_slice.as_slice()); - - AppLayerResult::ok() + state.parse_request(flow, stream_slice) } #[no_mangle] unsafe extern "C" fn SCLdapParseResponse( - _flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, + flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, _data: *const c_void, ) -> AppLayerResult { if stream_slice.is_empty() { @@ -334,9 +395,7 @@ unsafe extern "C" fn SCLdapParseResponse( } } let state = cast_pointer!(state, LdapState); - state.parse_response(stream_slice.as_slice()); - - AppLayerResult::ok() + state.parse_response(flow, stream_slice) } #[no_mangle] @@ -375,7 +434,7 @@ const PARSER_NAME: &[u8] = b"ldap\0"; #[no_mangle] pub unsafe extern "C" fn rs_ldap_register_parser() { let default_port = CString::new("389").unwrap(); - let parser = RustParser { + let mut parser = RustParser { name: PARSER_NAME.as_ptr() as *const c_char, default_port: default_port.as_ptr(), ipproto: IPPROTO_TCP, @@ -426,4 +485,17 @@ pub unsafe extern "C" fn rs_ldap_register_parser() { } else { SCLogDebug!("Protocol detection and parser disabled for LDAP."); } + + parser.ipproto = IPPROTO_UDP; + let ip_proto_str = CString::new("udp").unwrap(); + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_LDAP = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_LDAP); + } else { + SCLogDebug!("Protocol detection and parser disabled for LDAP."); + } } diff --git a/src/output.c b/src/output.c index 49b2c84ebffb..9c570586b67b 100644 --- a/src/output.c +++ b/src/output.c @@ -1094,7 +1094,7 @@ void OutputRegisterLoggers(void) JsonLogThreadDeinit, NULL); /* Ldap JSON logger. */ OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonLdapLog", "eve-log.ldap", - OutputJsonLogInitSub, ALPROTO_LDAP, JsonGenericDirPacketLogger, JsonLogThreadInit, + OutputJsonLogInitSub, ALPROTO_LDAP, JsonGenericDirFlowLogger, JsonLogThreadInit, JsonLogThreadDeinit, NULL); /* Template JSON logger. */ OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template",