From: Giuseppe Longo Date: Thu, 18 Jul 2024 14:00:59 +0000 (+0200) Subject: rust/ldap: enable parser for udp X-Git-Tag: suricata-8.0.0-beta1~952 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=edf70276d630954957e5f11a4fcb4645ea099bea;p=thirdparty%2Fsuricata.git rust/ldap: enable parser for udp This introduces a new parser registration function for LDAP/UDP, and update ldap configuration in order to be able to enable/disable a single parser independently (such as dns). Also, GAPs are accepted only for TCP parser and not for UDP. Ticket #7203 --- diff --git a/etc/schema.json b/etc/schema.json index a780590d9f..74439841a2 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -4640,8 +4640,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": { @@ -4811,8 +4815,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": "Number of flows LDAP/UDP protocol", "type": "integer" }, "modbus": { @@ -4977,8 +4985,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": "Number of transactions for LDAP/UDP protocol", "type": "integer" }, "modbus": { diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index 4744023036..420d0e9dc0 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -28,7 +28,9 @@ use std::os::raw::{c_char, c_int, c_void}; use crate::ldap::types::*; -static mut LDAP_MAX_TX: usize = 256; +static LDAP_MAX_TX_DEFAULT: usize = 256; + +static mut LDAP_MAX_TX: usize = LDAP_MAX_TX_DEFAULT; static mut ALPROTO_LDAP: AppProto = ALPROTO_UNKNOWN; @@ -37,6 +39,7 @@ enum LdapEvent { TooManyTransactions, InvalidData, RequestNotFound, + IncompleteData, } #[derive(Debug)] @@ -171,10 +174,7 @@ impl LdapState { start = rem; let mut tx = self.new_tx(); let request = LdapMessage::from(msg); - tx.complete = match request.protocol_op { - ProtocolOp::UnbindRequest => true, - _ => false, - }; + tx.complete = tx_is_complete(&request.protocol_op, Direction::ToServer); tx.request = Some(request); self.transactions.push_back(tx); } @@ -204,17 +204,7 @@ impl LdapState { let response = LdapMessage::from(msg); if let Some(tx) = self.find_request(response.message_id) { - tx.complete = match response.protocol_op { - ProtocolOp::SearchResultDone(_) - | ProtocolOp::BindResponse(_) - | ProtocolOp::ModifyResponse(_) - | ProtocolOp::AddResponse(_) - | ProtocolOp::DelResponse(_) - | ProtocolOp::ModDnResponse(_) - | ProtocolOp::CompareResponse(_) - | ProtocolOp::ExtendedResponse(_) => true, - _ => false, - }; + tx.complete = tx_is_complete(&response.protocol_op, Direction::ToClient); tx.responses.push_back(response); } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { // this is an unsolicited notification, which means @@ -245,6 +235,98 @@ impl LdapState { return AppLayerResult::ok(); } + + fn parse_request_udp( + &mut self, _flow: *const Flow, stream_slice: StreamSlice, + ) -> AppLayerResult { + let input = stream_slice.as_slice(); + + match ldap_parse_msg(input) { + Ok((_, msg)) => { + let mut tx = self.new_tx(); + let request = LdapMessage::from(msg); + tx.complete = tx_is_complete(&request.protocol_op, Direction::ToServer); + tx.request = Some(request); + self.transactions.push_back(tx); + } + Err(nom::Err::Incomplete(_)) => { + self.set_event(LdapEvent::IncompleteData); + return AppLayerResult::err(); + } + Err(_) => { + self.set_event(LdapEvent::InvalidData); + return AppLayerResult::err(); + } + } + + return AppLayerResult::ok(); + } + + fn parse_response_udp( + &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() { + match ldap_parse_msg(start) { + Ok((rem, msg)) => { + let response = LdapMessage::from(msg); + if let Some(tx) = self.find_request(response.message_id) { + tx.complete = tx_is_complete(&response.protocol_op, Direction::ToClient); + tx.responses.push_back(response); + } 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(); + tx.complete = true; + tx.responses.push_back(response); + self.transactions.push_back(tx); + } else { + let mut tx = self.new_tx(); + tx.complete = true; + tx.responses.push_back(response); + self.transactions.push_back(tx); + self.set_event(LdapEvent::RequestNotFound); + }; + start = rem; + } + Err(nom::Err::Incomplete(_)) => { + self.set_event(LdapEvent::IncompleteData); + return AppLayerResult::err(); + } + Err(_) => { + self.set_event(LdapEvent::InvalidData); + return AppLayerResult::err(); + } + } + } + + return AppLayerResult::ok(); + } +} + +fn tx_is_complete(op: &ProtocolOp, dir: Direction) -> bool { + match dir { + Direction::ToServer => match op { + ProtocolOp::UnbindRequest => true, + _ => false, + }, + Direction::ToClient => match op { + ProtocolOp::SearchResultDone(_) + | ProtocolOp::BindResponse(_) + | ProtocolOp::ModifyResponse(_) + | ProtocolOp::AddResponse(_) + | ProtocolOp::DelResponse(_) + | ProtocolOp::ModDnResponse(_) + | ProtocolOp::CompareResponse(_) + | ProtocolOp::ExtendedResponse(_) => true, + _ => false, + }, + } } fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> AppProto { @@ -340,6 +422,24 @@ unsafe extern "C" fn SCLdapParseResponse( AppLayerResult::ok() } +#[no_mangle] +unsafe extern "C" fn SCLdapParseRequestUDP( + flow: *const Flow, state: *mut c_void, _pstate: *mut c_void, stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, LdapState); + state.parse_request_udp(flow, stream_slice) +} + +#[no_mangle] +unsafe extern "C" fn SCLdapParseResponseUDP( + flow: *const Flow, state: *mut c_void, _pstate: *mut c_void, stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, LdapState); + state.parse_response_udp(flow, stream_slice) +} + #[no_mangle] unsafe extern "C" fn SCLdapStateGetTx(state: *mut c_void, tx_id: u64) -> *mut c_void { let state = cast_pointer!(state, LdapState); @@ -374,7 +474,7 @@ export_state_data_get!(SCLdapGetStateData, LdapState); const PARSER_NAME: &[u8] = b"ldap\0"; #[no_mangle] -pub unsafe extern "C" fn rs_ldap_register_parser() { +pub unsafe extern "C" fn SCRegisterLdapTcpParser() { let default_port = CString::new("389").unwrap(); let parser = RustParser { name: PARSER_NAME.as_ptr() as *const c_char, @@ -409,7 +509,6 @@ pub unsafe extern "C" fn rs_ldap_register_parser() { }; let ip_proto_str = CString::new("tcp").unwrap(); - if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { let alproto = AppLayerRegisterProtocolDetection(&parser, 1); ALPROTO_LDAP = alproto; @@ -418,13 +517,72 @@ pub unsafe extern "C" fn rs_ldap_register_parser() { } if let Some(val) = conf_get("app-layer.protocols.ldap.max-tx") { if let Ok(v) = val.parse::() { - LDAP_MAX_TX = v; + if LDAP_MAX_TX == LDAP_MAX_TX_DEFAULT { + LDAP_MAX_TX = v; + } } else { SCLogError!("Invalid value for ldap.max-tx"); } } AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_LDAP); } else { - SCLogDebug!("Protocol detection and parser disabled for LDAP."); + SCLogDebug!("Protocol detection and parser disabled for LDAP/TCP."); + } +} + +#[no_mangle] +pub unsafe extern "C" fn SCRegisterLdapUdpParser() { + let default_port = CString::new("389").unwrap(); + let parser = RustParser { + name: PARSER_NAME.as_ptr() as *const c_char, + default_port: default_port.as_ptr(), + ipproto: IPPROTO_UDP, + probe_ts: Some(SCLdapProbingParser), + probe_tc: Some(SCLdapProbingParser), + min_depth: 0, + max_depth: 16, + state_new: SCLdapStateNew, + state_free: SCLdapStateFree, + tx_free: SCLdapStateTxFree, + parse_ts: SCLdapParseRequestUDP, + parse_tc: SCLdapParseResponseUDP, + get_tx_count: SCLdapStateGetTxCount, + get_tx: SCLdapStateGetTx, + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: SCLdapTxGetAlstateProgress, + get_eventinfo: Some(LdapEvent::get_event_info), + get_eventinfo_byid: Some(LdapEvent::get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + get_tx_iterator: Some(applayer::state_get_tx_iterator::), + get_tx_data: SCLdapGetTxData, + get_state_data: SCLdapGetStateData, + apply_tx_config: None, + flags: 0, + get_frame_id_by_name: None, + get_frame_name_by_id: None, + }; + + 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); + } + if let Some(val) = conf_get("app-layer.protocols.ldap.max-tx") { + if let Ok(v) = val.parse::() { + if LDAP_MAX_TX == LDAP_MAX_TX_DEFAULT { + LDAP_MAX_TX = v; + } + } else { + SCLogError!("Invalid value for ldap.max-tx"); + } + } + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_LDAP); + } else { + SCLogDebug!("Protocol detection and parser disabled for LDAP/UDP."); } } diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index b9eecd89b9..bb1666a28c 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1722,7 +1722,8 @@ void AppLayerParserRegisterProtocolParsers(void) rs_sip_register_parser(); rs_quic_register_parser(); rs_websocket_register_parser(); - rs_ldap_register_parser(); + SCRegisterLdapTcpParser(); + SCRegisterLdapUdpParser(); rs_template_register_parser(); SCRfbRegisterParser(); SCMqttRegisterParser(); diff --git a/src/output.c b/src/output.c index 6d24a8d498..b18832afa5 100644 --- a/src/output.c +++ b/src/output.c @@ -1145,7 +1145,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); /* DoH2 JSON logger. */ JsonDoh2LogRegister(); diff --git a/suricata.yaml.in b/suricata.yaml.in index fae02ce7ac..a54c337524 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1189,7 +1189,14 @@ app-layer: #enabled: yes ldap: - enabled: yes + tcp: + enabled: yes + detection-ports: + dp: 389 + udp: + enabled: yes + detection-ports: + dp: 389 # Maximum number of live LDAP transactions per flow # max-tx: 1024