]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rust/ldap: enable parser for udp
authorGiuseppe Longo <giuseppe@glongo.it>
Thu, 18 Jul 2024 14:00:59 +0000 (16:00 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 7 Aug 2024 17:04:35 +0000 (19:04 +0200)
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

etc/schema.json
rust/src/ldap/ldap.rs
src/app-layer-parser.c
src/output.c
suricata.yaml.in

index a780590d9f2dd8145df1f51d972288f24a4779a3..74439841a22feaae3ea6b5de5e9ff13ca46b106c 100644 (file)
                                             "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": {
                                     "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": {
                                             "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": {
index 47440230364a332f4b57c34fb6aaebd7f9f21e40..420d0e9dc0ddcf1f76d2f7055bc2b7c02a08808d 100644 (file)
@@ -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::<usize>() {
-                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::<LdapState, LdapTransaction>),
+        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::<usize>() {
+                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.");
     }
 }
index b9eecd89b98e1dcaddaa2d64ead3081a71de703e..bb1666a28c2ed6182b0734ed48eaeb61b21ecb80 100644 (file)
@@ -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();
index 6d24a8d498fb11f3a165a8292670bdb37f32140c..b18832afa524393b4903dc6e3549e97711f15a1b 100644 (file)
@@ -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();
index fae02ce7ac08ad2f0532313eb2a8a0d205c97ed9..a54c337524b5f2d1e5ca896337980c304936c1fd 100644 (file)
@@ -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