Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rust/ldap: udp and frames support v2 #11535

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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": {
Expand Down Expand Up @@ -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": {
Expand Down
102 changes: 87 additions & 15 deletions rust/src/ldap/ldap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -79,6 +85,8 @@ pub struct LdapState {
tx_id: u64,
transactions: VecDeque<LdapTransaction>,
tx_index_completed: usize,
request_frame: Option<Frame>,
response_frame: Option<Frame>,
}

impl State<LdapTransaction> for LdapState {
Expand All @@ -98,6 +106,8 @@ impl LdapState {
tx_id: 0,
transactions: VecDeque::new(),
tx_index_completed: 0,
request_frame: None,
response_frame: None,
}
}

Expand Down Expand Up @@ -158,24 +168,44 @@ 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,
_ => false,
};
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();
Expand All @@ -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 {
Expand All @@ -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();
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand All @@ -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]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.");
}
}
2 changes: 1 addition & 1 deletion src/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading