From: Pierre Chifflier Date: Wed, 4 Jun 2025 12:36:46 +0000 (+0200) Subject: ldap: factorize code and remove duplicated structs, use ldap_parser where relevant X-Git-Tag: suricata-8.0.0-rc1~81 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bda22c1f4a4c1ba063dd676749a7454edd067348;p=thirdparty%2Fsuricata.git ldap: factorize code and remove duplicated structs, use ldap_parser where relevant --- diff --git a/rust/src/ldap/detect.rs b/rust/src/ldap/detect.rs index 8942e573f2..cd3d408733 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -22,7 +22,8 @@ use crate::detect::uint::{ SCDetectU8Free, }; use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer}; -use crate::ldap::types::{LdapMessage, LdapResultCode, ProtocolOp, ProtocolOpCode}; +use crate::ldap::types::*; +use ldap_parser::ldap::{LdapMessage, ProtocolOp}; use suricata_sys::sys::{ DetectEngineCtx, DetectEngineThreadCtx, Flow, SCDetectBufferSetActiveList, SCDetectHelperBufferMpmRegister, SCDetectHelperBufferRegister, SCDetectHelperKeywordRegister, @@ -120,7 +121,7 @@ unsafe extern "C" fn ldap_detect_request_operation_match( let tx = cast_pointer!(tx, LdapTransaction); let ctx = cast_pointer!(ctx, DetectUintData); if let Some(request) = &tx.request { - let option = request.protocol_op.to_u8(); + let option = request.protocol_op.tag().0 as u8; return detect_match_uint(ctx, option) as c_int; } return 0; @@ -251,7 +252,7 @@ unsafe extern "C" fn ldap_detect_responses_operation_match( return match_at_index::( &tx.responses, &ctx.du8, - |response| Some(response.protocol_op.to_u8()), + |response| Some(response.protocol_op.tag().0 as u8), |code, ctx_value| detect_match_uint(ctx_value, code) as c_int, &ctx.index, ); @@ -326,13 +327,13 @@ unsafe extern "C" fn ldap_detect_request_dn_get_data( if let Some(request) = &tx.request { let str_buffer: &str = match &request.protocol_op { - ProtocolOp::BindRequest(req) => req.name.0.as_str(), - ProtocolOp::AddRequest(req) => req.entry.0.as_str(), - ProtocolOp::SearchRequest(req) => req.base_object.0.as_str(), - ProtocolOp::ModifyRequest(req) => req.object.0.as_str(), - ProtocolOp::DelRequest(req) => req.0.as_str(), - ProtocolOp::ModDnRequest(req) => req.entry.0.as_str(), - ProtocolOp::CompareRequest(req) => req.entry.0.as_str(), + ProtocolOp::BindRequest(req) => req.name.0.as_ref(), + ProtocolOp::AddRequest(req) => req.entry.0.as_ref(), + ProtocolOp::SearchRequest(req) => req.base_object.0.as_ref(), + ProtocolOp::ModifyRequest(req) => req.object.0.as_ref(), + ProtocolOp::DelRequest(req) => req.0.as_ref(), + ProtocolOp::ModDnRequest(req) => req.entry.0.as_ref(), + ProtocolOp::CompareRequest(req) => req.entry.0.as_ref(), _ => return false, }; *buffer = str_buffer.as_ptr(); @@ -369,15 +370,15 @@ unsafe extern "C" fn ldap_tx_get_responses_dn( let response = &tx.responses[local_id as usize]; // We expect every response in one tx to be the same protocol_op let str_buffer: &str = match &response.protocol_op { - ProtocolOp::SearchResultEntry(resp) => resp.object_name.0.as_str(), - ProtocolOp::BindResponse(resp) => resp.result.matched_dn.0.as_str(), - ProtocolOp::SearchResultDone(resp) => resp.matched_dn.0.as_str(), - ProtocolOp::ModifyResponse(resp) => resp.result.matched_dn.0.as_str(), - ProtocolOp::AddResponse(resp) => resp.matched_dn.0.as_str(), - ProtocolOp::DelResponse(resp) => resp.matched_dn.0.as_str(), - ProtocolOp::ModDnResponse(resp) => resp.matched_dn.0.as_str(), - ProtocolOp::CompareResponse(resp) => resp.matched_dn.0.as_str(), - ProtocolOp::ExtendedResponse(resp) => resp.result.matched_dn.0.as_str(), + ProtocolOp::SearchResultEntry(resp) => resp.object_name.0.as_ref(), + ProtocolOp::BindResponse(resp) => resp.result.matched_dn.0.as_ref(), + ProtocolOp::SearchResultDone(resp) => resp.matched_dn.0.as_ref(), + ProtocolOp::ModifyResponse(resp) => resp.result.matched_dn.0.as_ref(), + ProtocolOp::AddResponse(resp) => resp.matched_dn.0.as_ref(), + ProtocolOp::DelResponse(resp) => resp.matched_dn.0.as_ref(), + ProtocolOp::ModDnResponse(resp) => resp.matched_dn.0.as_ref(), + ProtocolOp::CompareResponse(resp) => resp.matched_dn.0.as_ref(), + ProtocolOp::ExtendedResponse(resp) => resp.result.matched_dn.0.as_ref(), _ => "", // This ensures that the iteration continues, // allowing other responses in the transaction to be processed correctly @@ -503,14 +504,14 @@ unsafe extern "C" fn ldap_tx_get_responses_msg( let response = &tx.responses[local_id as usize]; // We expect every response in one tx to be the same protocol_op let str_buffer: &str = match &response.protocol_op { - ProtocolOp::BindResponse(resp) => resp.result.diagnostic_message.0.as_str(), - ProtocolOp::SearchResultDone(resp) => resp.diagnostic_message.0.as_str(), - ProtocolOp::ModifyResponse(resp) => resp.result.diagnostic_message.0.as_str(), - ProtocolOp::AddResponse(resp) => resp.diagnostic_message.0.as_str(), - ProtocolOp::DelResponse(resp) => resp.diagnostic_message.0.as_str(), - ProtocolOp::ModDnResponse(resp) => resp.diagnostic_message.0.as_str(), - ProtocolOp::CompareResponse(resp) => resp.diagnostic_message.0.as_str(), - ProtocolOp::ExtendedResponse(resp) => resp.result.diagnostic_message.0.as_str(), + ProtocolOp::BindResponse(resp) => resp.result.diagnostic_message.0.as_ref(), + ProtocolOp::SearchResultDone(resp) => resp.diagnostic_message.0.as_ref(), + ProtocolOp::ModifyResponse(resp) => resp.result.diagnostic_message.0.as_ref(), + ProtocolOp::AddResponse(resp) => resp.diagnostic_message.0.as_ref(), + ProtocolOp::DelResponse(resp) => resp.diagnostic_message.0.as_ref(), + ProtocolOp::ModDnResponse(resp) => resp.diagnostic_message.0.as_ref(), + ProtocolOp::CompareResponse(resp) => resp.diagnostic_message.0.as_ref(), + ProtocolOp::ExtendedResponse(resp) => resp.result.diagnostic_message.0.as_ref(), _ => "", // This ensures that the iteration continues, // allowing other responses in the transaction to be processed correctly @@ -547,7 +548,7 @@ unsafe extern "C" fn ldap_tx_get_req_attribute_type( if local_id as usize >= req.attributes.len() { return false; } - req.attributes[local_id as usize].0.as_str() + req.attributes[local_id as usize].0.as_ref() } ProtocolOp::ModifyRequest(req) => { if local_id as usize >= req.changes.len() { @@ -557,19 +558,19 @@ unsafe extern "C" fn ldap_tx_get_req_attribute_type( .modification .attr_type .0 - .as_str() + .as_ref() } ProtocolOp::AddRequest(req) => { if local_id as usize >= req.attributes.len() { return false; } - req.attributes[local_id as usize].attr_type.0.as_str() + req.attributes[local_id as usize].attr_type.0.as_ref() } ProtocolOp::CompareRequest(req) => { if local_id > 0 { return false; } - req.ava.attribute_desc.0.as_str() + req.ava.attribute_desc.0.as_ref() } _ => return false, }; diff --git a/rust/src/ldap/filters.rs b/rust/src/ldap/filters.rs deleted file mode 100644 index caa846767f..0000000000 --- a/rust/src/ldap/filters.rs +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright (C) 2024 Open Information Security Foundation - * - * You can copy, redistribute or modify this Program under the terms of - * the GNU General Public License version 2 as published by the Free - * Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * version 2 along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -// written by Giuseppe Longo - -use crate::ldap::types::LdapString; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Filter { - And(Vec), - Or(Vec), - Not(Box), - EqualityMatch(AttributeValueAssertion), - Substrings(SubstringFilter), - GreaterOrEqual(AttributeValueAssertion), - LessOrEqual(AttributeValueAssertion), - Present(LdapString), - ApproxMatch(AttributeValueAssertion), - ExtensibleMatch(MatchingRuleAssertion), -} - -impl From> for Filter { - fn from(f: ldap_parser::filter::Filter) -> Self { - match f { - ldap_parser::filter::Filter::And(val) => { - let mut vec = Vec::new(); - for filter in val { - vec.push(filter.into()); - } - Filter::And(vec) - } - ldap_parser::filter::Filter::Or(val) => { - let mut vec = Vec::new(); - for filter in val { - vec.push(filter.into()); - } - Filter::Or(vec) - } - ldap_parser::filter::Filter::Not(val) => { - let f = *val; - let f2: Filter = f.into(); - Filter::Not(Box::from(f2)) - } - ldap_parser::filter::Filter::EqualityMatch(val) => { - Filter::EqualityMatch(AttributeValueAssertion { - attribute_desc: LdapString(val.attribute_desc.0.to_string()), - assertion_value: val.assertion_value.to_vec(), - }) - } - ldap_parser::filter::Filter::Substrings(val) => { - let filter_type = LdapString(val.filter_type.0.to_string()); - let mut substrings: Vec = Vec::new(); - for s in val.substrings { - substrings.push(s.into()); - } - Filter::Substrings(SubstringFilter { - filter_type, - substrings, - }) - } - ldap_parser::filter::Filter::GreaterOrEqual(val) => { - Filter::GreaterOrEqual(AttributeValueAssertion { - attribute_desc: LdapString(val.attribute_desc.0.to_string()), - assertion_value: val.assertion_value.to_vec(), - }) - } - ldap_parser::filter::Filter::LessOrEqual(val) => { - Filter::LessOrEqual(AttributeValueAssertion { - attribute_desc: LdapString(val.attribute_desc.0.to_string()), - assertion_value: val.assertion_value.to_vec(), - }) - } - ldap_parser::filter::Filter::Present(val) => { - Filter::Present(LdapString(val.0.to_string())) - } - ldap_parser::filter::Filter::ApproxMatch(val) => { - Filter::ApproxMatch(AttributeValueAssertion { - attribute_desc: LdapString(val.attribute_desc.0.to_string()), - assertion_value: val.assertion_value.to_vec(), - }) - } - ldap_parser::filter::Filter::ExtensibleMatch(val) => { - let matching_rule = if let Some(mr) = val.matching_rule { - Some(LdapString(mr.0.to_string())) - } else { - None - }; - let rule_type = if let Some(rt) = val.rule_type { - Some(AttributeDescription(rt.0.to_string())) - } else { - None - }; - let assertion_value = AssertionValue(val.assertion_value.0.to_vec()); - let dn_attributes = val.dn_attributes; - Filter::ExtensibleMatch(MatchingRuleAssertion { - matching_rule, - rule_type, - assertion_value, - dn_attributes, - }) - } - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PartialAttribute { - pub attr_type: LdapString, - pub attr_vals: Vec, -} - -impl From<&ldap_parser::filter::PartialAttribute<'_>> for PartialAttribute { - fn from(value: &ldap_parser::filter::PartialAttribute) -> Self { - let attr_type = LdapString(value.attr_type.0.to_string()); - let attr_vals: Vec = value - .attr_vals - .iter() - .map(|a| AttributeValue(a.0.to_vec())) - .collect(); - - Self { - attr_type, - attr_vals, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Attribute { - pub attr_type: LdapString, - pub attr_vals: Vec, -} - -impl From<&ldap_parser::filter::Attribute<'_>> for Attribute { - fn from(value: &ldap_parser::filter::Attribute) -> Self { - let attr_type = LdapString(value.attr_type.0.to_string()); - let attr_vals: Vec = value - .attr_vals - .iter() - .map(|a| AttributeValue(a.0.to_vec())) - .collect(); - - Self { - attr_type, - attr_vals, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AttributeValueAssertion { - pub attribute_desc: LdapString, - pub assertion_value: Vec, -} -impl From<&ldap_parser::filter::AttributeValueAssertion<'_>> for AttributeValueAssertion { - fn from(value: &ldap_parser::filter::AttributeValueAssertion) -> Self { - let attribute_desc = LdapString(value.attribute_desc.0.to_string()); - let assertion_value = value.assertion_value.to_vec(); - Self { - attribute_desc, - assertion_value, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AttributeDescription(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MatchingRuleAssertion { - pub matching_rule: Option, - pub rule_type: Option, - pub assertion_value: AssertionValue, - pub dn_attributes: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct MatchingRuleId(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SubstringFilter { - pub filter_type: LdapString, - pub substrings: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Substring { - Initial(AssertionValue), - Any(AssertionValue), - Final(AssertionValue), -} -impl From> for Substring { - fn from(value: ldap_parser::filter::Substring) -> Self { - match value { - ldap_parser::filter::Substring::Initial(val) => { - Substring::Initial(AssertionValue(val.0.to_vec())) - } - ldap_parser::filter::Substring::Any(val) => { - Substring::Any(AssertionValue(val.0.to_vec())) - } - ldap_parser::filter::Substring::Final(val) => { - Substring::Final(AssertionValue(val.0.to_vec())) - } - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AssertionValue(pub Vec); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AttributeValue(pub Vec); diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index 0b7e078343..e59e36cefd 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -15,7 +15,8 @@ * 02110-1301, USA. */ -// written by Giuseppe Longo +// Author: Giuseppe Longo +// Author: Pierre Chifflier use crate::applayer::{self, *}; use crate::conf::conf_get; @@ -23,6 +24,7 @@ use crate::core::*; use crate::direction::Direction; use crate::flow::Flow; use crate::frames::*; +use ldap_parser::asn1_rs::ToStatic; use nom7 as nom; use std; use std::collections::VecDeque; @@ -34,7 +36,8 @@ use suricata_sys::sys::{ SCAppLayerProtoDetectConfProtoDetectionEnabled, SCAppLayerRequestProtocolTLSUpgrade, }; -use crate::ldap::types::*; +use super::types::*; +use ldap_parser::ldap::*; static LDAP_MAX_TX_DEFAULT: usize = 256; @@ -60,8 +63,8 @@ enum LdapEvent { #[derive(Debug)] pub struct LdapTransaction { pub tx_id: u64, - pub request: Option, - pub responses: VecDeque, + pub request: Option>, + pub responses: VecDeque>, complete: bool, tx_data: AppLayerTxData, @@ -239,7 +242,7 @@ impl LdapState { } } tx.complete |= tx_is_complete(&request.protocol_op, Direction::ToServer); - tx.request = Some(request); + tx.request = Some(request.to_static()); self.transactions.push_back(tx); sc_app_layer_parser_trigger_raw_stream_inspection( flow, @@ -311,7 +314,7 @@ impl LdapState { tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); let tx_id = tx.id(); tx.tx_data.updated_tc = true; - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); sc_app_layer_parser_trigger_raw_stream_inspection( flow, Direction::ToClient as i32, @@ -328,7 +331,7 @@ impl LdapState { let mut tx = tx.unwrap(); let tx_id = tx.id(); tx.complete = true; - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); self.transactions.push_back(tx); sc_app_layer_parser_trigger_raw_stream_inspection( flow, @@ -344,7 +347,7 @@ impl LdapState { let mut tx = tx.unwrap(); tx.complete = true; let tx_id = tx.id(); - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); self.transactions.push_back(tx); sc_app_layer_parser_trigger_raw_stream_inspection( flow, @@ -394,7 +397,7 @@ impl LdapState { let mut tx = tx.unwrap(); let request = LdapMessage::from(msg); tx.complete |= tx_is_complete(&request.protocol_op, Direction::ToServer); - tx.request = Some(request); + tx.request = Some(request.to_static()); self.transactions.push_back(tx); } Err(nom::Err::Incomplete(_)) => { @@ -437,7 +440,7 @@ impl LdapState { if let Some(tx) = self.find_request(response.message_id) { tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); let tx_id = tx.id(); - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); let consumed = start.len() - rem.len(); self.set_frame_tc(flow, tx_id, consumed as i64); } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { @@ -450,7 +453,7 @@ impl LdapState { let mut tx = tx.unwrap(); tx.complete = true; let tx_id = tx.id(); - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); self.transactions.push_back(tx); let consumed = start.len() - rem.len(); self.set_frame_tc(flow, tx_id, consumed as i64); @@ -462,7 +465,7 @@ impl LdapState { let mut tx = tx.unwrap(); tx.complete = true; let tx_id = tx.id(); - tx.responses.push_back(response); + tx.responses.push_back(response.to_static()); self.transactions.push_back(tx); self.set_event(LdapEvent::RequestNotFound); let consumed = start.len() - rem.len(); @@ -533,12 +536,12 @@ fn probe(input: &[u8], direction: Direction, rdir: *mut u8) -> AppProto { match ldap_parse_msg(input) { Ok((_, msg)) => { let ldap_msg = LdapMessage::from(msg); - if direction == Direction::ToServer && !ldap_msg.is_request() { + if direction == Direction::ToServer && !ldap_is_request(&ldap_msg) { unsafe { *rdir = Direction::ToClient.into(); } } - if direction == Direction::ToClient && !ldap_msg.is_response() { + if direction == Direction::ToClient && !ldap_is_response(&ldap_msg) { unsafe { *rdir = Direction::ToServer.into(); } diff --git a/rust/src/ldap/logger.rs b/rust/src/ldap/logger.rs index 57557f3a40..227cb4c262 100644 --- a/rust/src/ldap/logger.rs +++ b/rust/src/ldap/logger.rs @@ -19,18 +19,19 @@ use crate::detect::EnumString; use crate::jsonbuilder::{JsonBuilder, JsonError}; -use crate::ldap::filters::*; use crate::ldap::ldap::LdapTransaction; use crate::ldap::types::*; +use ldap_parser::filter::*; +use ldap_parser::ldap::*; fn log_ldap(tx: &LdapTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { js.open_object("ldap")?; if let Some(req) = &tx.request { - let protocol_op_str = req.protocol_op.to_string(); + let protocol_op_str = ldap_protocol_op_as_str(&req.protocol_op); js.open_object("request")?; js.set_uint("message_id", req.message_id.0)?; - js.set_string("operation", &protocol_op_str)?; + js.set_string("operation", protocol_op_str)?; match &req.protocol_op { ProtocolOp::SearchRequest(msg) => log_search_request(msg, js)?, @@ -56,8 +57,8 @@ fn log_ldap(tx: &LdapTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> for response in &tx.responses { js.start_object()?; - let protocol_op_str = response.protocol_op.to_string(); - js.set_string("operation", &protocol_op_str)?; + let protocol_op_str = ldap_protocol_op_as_str(&response.protocol_op); + js.set_string("operation", protocol_op_str)?; if tx.request.is_none() { js.set_uint("message_id", response.message_id.0)?; @@ -97,7 +98,7 @@ fn log_search_request(msg: &SearchRequest, js: &mut JsonBuilder) -> Result<(), J if let Filter::Present(val) = &msg.filter { js.open_object("filter")?; js.set_string("type", "present")?; - js.set_string("value", &val.0.to_string())?; + js.set_string("value", &val.0)?; js.close()?; } if !msg.attributes.is_empty() { @@ -135,7 +136,10 @@ fn log_modify_request(msg: &ModifyRequest, js: &mut JsonBuilder) -> Result<(), J js.open_array("changes")?; for change in &msg.changes { js.start_object()?; - js.set_string("operation", &change.operation.to_string())?; + js.set_string( + "operation", + ldap_operation_to_string(&change.operation).as_str(), + )?; js.open_object("modification")?; js.set_string("attribute_type", &change.modification.attr_type.0)?; if !change.modification.attr_vals.is_empty() { diff --git a/rust/src/ldap/mod.rs b/rust/src/ldap/mod.rs index 723d7e87fb..366c8b6c68 100644 --- a/rust/src/ldap/mod.rs +++ b/rust/src/ldap/mod.rs @@ -18,7 +18,6 @@ // written by Giuseppe Longo pub mod detect; -pub mod filters; pub mod ldap; pub mod logger; pub mod types; diff --git a/rust/src/ldap/types.rs b/rust/src/ldap/types.rs index 8ee2ccfa0f..08a94667af 100644 --- a/rust/src/ldap/types.rs +++ b/rust/src/ldap/types.rs @@ -15,188 +15,12 @@ * 02110-1301, USA. */ -// written by Giuseppe Longo - -use std::convert::From; -use std::fmt; -use std::fmt::{Display, Formatter}; +// Author: Giuseppe Longo +// Author: Pierre Chifflier use ldap_parser::asn1_rs::{FromBer, ParseResult}; use ldap_parser::error::LdapError; - -use crate::ldap::filters::*; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Operation(pub u32); - -impl Display for Operation { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.0 { - 0 => write!(f, "add"), - 1 => write!(f, "delete"), - 2 => write!(f, "replace"), - _ => write!(f, "{}", self.0), - } - } -} - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -pub struct ResultCode(pub u32); - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -pub struct MessageID(pub u32); - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct SearchScope(pub u32); - -impl Display for SearchScope { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.0 { - 0 => write!(f, "base_object"), - 1 => write!(f, "single_level"), - 2 => write!(f, "whole_subtree"), - _ => write!(f, "{}", self.0), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct DerefAliases(pub u32); - -impl Display for DerefAliases { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.0 { - 0 => write!(f, "never_deref_aliases"), - 1 => write!(f, "deref_in_searching"), - 2 => write!(f, "deref_finding_base_obj"), - 3 => write!(f, "deref_always"), - _ => write!(f, "{}", self.0), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct LdapString(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct LdapDN(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct RelativeLdapDN(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct LdapOID(pub String); - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct LdapResult { - pub result_code: ResultCode, - pub matched_dn: LdapDN, - pub diagnostic_message: LdapString, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BindRequest { - pub version: u8, - pub name: LdapDN, - pub authentication: AuthenticationChoice, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SaslCredentials { - pub mechanism: LdapString, - pub credentials: Option>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum AuthenticationChoice { - Simple(Vec), - Sasl(SaslCredentials), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BindResponse { - pub result: LdapResult, - pub server_sasl_creds: Option>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchRequest { - pub base_object: LdapDN, - pub scope: SearchScope, - pub deref_aliases: DerefAliases, - pub size_limit: u32, - pub time_limit: u32, - pub types_only: bool, - pub filter: Filter, - pub attributes: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SearchResultEntry { - pub object_name: LdapDN, - pub attributes: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ModifyRequest { - pub object: LdapDN, - pub changes: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ModifyResponse { - pub result: LdapResult, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Change { - pub operation: Operation, - pub modification: PartialAttribute, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AddRequest { - pub entry: LdapDN, - pub attributes: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ModDnRequest { - pub entry: LdapDN, - pub newrdn: RelativeLdapDN, - pub deleteoldrdn: bool, - pub newsuperior: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct CompareRequest { - pub entry: LdapDN, - pub ava: AttributeValueAssertion, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AbandonRequest { - pub message_id: u32, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ExtendedRequest { - pub request_name: LdapOID, - pub request_value: Option>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ExtendedResponse { - pub result: LdapResult, - pub response_name: Option, - pub response_value: Option>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct IntermediateResponse { - pub response_name: Option, - pub response_value: Option>, -} +use ldap_parser::ldap::{LdapMessage, Operation, ProtocolOp}; #[derive(Clone, Debug, EnumStringU32)] #[repr(u32)] @@ -277,31 +101,6 @@ pub enum LdapResultCode { NoOperation = 16654, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ProtocolOp { - BindRequest(BindRequest), - BindResponse(BindResponse), - UnbindRequest, - SearchRequest(SearchRequest), - SearchResultEntry(SearchResultEntry), - SearchResultDone(LdapResult), - SearchResultReference(Vec), - ModifyRequest(ModifyRequest), - ModifyResponse(ModifyResponse), - AddRequest(AddRequest), - AddResponse(LdapResult), - DelRequest(LdapDN), - DelResponse(LdapResult), - ModDnRequest(ModDnRequest), - ModDnResponse(LdapResult), - CompareRequest(CompareRequest), - CompareResponse(LdapResult), - ExtendedRequest(ExtendedRequest), - ExtendedResponse(ExtendedResponse), - IntermediateResponse(IntermediateResponse), - AbandonRequest(AbandonRequest), -} - #[derive(Clone, Debug, Default, EnumStringU8)] #[repr(u8)] pub enum ProtocolOpCode { @@ -329,357 +128,66 @@ pub enum ProtocolOpCode { IntermediateResponse = 25, } -impl ProtocolOp { - pub fn to_u8(&self) -> u8 { - match self { - ProtocolOp::BindRequest(_) => 0, - ProtocolOp::BindResponse(_) => 1, - ProtocolOp::UnbindRequest => 2, - ProtocolOp::SearchRequest(_) => 3, - ProtocolOp::SearchResultEntry(_) => 4, - ProtocolOp::SearchResultDone(_) => 5, - ProtocolOp::SearchResultReference(_) => 19, - ProtocolOp::ModifyRequest(_) => 6, - ProtocolOp::ModifyResponse(_) => 7, - ProtocolOp::AddRequest(_) => 8, - ProtocolOp::AddResponse(_) => 9, - ProtocolOp::DelRequest(_) => 10, - ProtocolOp::DelResponse(_) => 11, - ProtocolOp::ModDnRequest(_) => 12, - ProtocolOp::ModDnResponse(_) => 13, - ProtocolOp::CompareRequest(_) => 14, - ProtocolOp::CompareResponse(_) => 15, - ProtocolOp::AbandonRequest(_) => 16, - ProtocolOp::ExtendedRequest(_) => 23, - ProtocolOp::ExtendedResponse(_) => 24, - ProtocolOp::IntermediateResponse(_) => 25, +pub fn ldap_operation_to_string(op: &Operation) -> String { + match op.0 { + 0 => "add".to_string(), + 1 => "delete".to_string(), + 2 => "replace".to_string(), + _ => op.0.to_string(), + } +} + +pub fn ldap_protocol_op_as_str(op: &ProtocolOp) -> &'static str { + match op { + ProtocolOp::BindRequest(_) => "bind_request", + ProtocolOp::BindResponse(_) => "bind_response", + ProtocolOp::UnbindRequest => "unbind_request", + ProtocolOp::SearchRequest(_) => "search_request", + ProtocolOp::SearchResultEntry(_) => "search_result_entry", + ProtocolOp::SearchResultDone(_) => "search_result_done", + ProtocolOp::SearchResultReference(_) => "search_result_reference", + ProtocolOp::ModifyRequest(_) => "modify_request", + ProtocolOp::ModifyResponse(_) => "modify_response", + ProtocolOp::AddRequest(_) => "add_request", + ProtocolOp::AddResponse(_) => "add_response", + ProtocolOp::DelRequest(_) => "del_request", + ProtocolOp::DelResponse(_) => "del_response", + ProtocolOp::ModDnRequest(_) => "mod_dn_request", + ProtocolOp::ModDnResponse(_) => "mod_dn_response", + ProtocolOp::CompareRequest(_) => "compare_request", + ProtocolOp::CompareResponse(_) => "compare_response", + ProtocolOp::AbandonRequest(_) => "abandon_request", + ProtocolOp::ExtendedRequest(_) => "extended_request", + ProtocolOp::ExtendedResponse(_) => "extended_response", + ProtocolOp::IntermediateResponse(_) => "intermediate_response", + } +} + +pub fn ldap_is_request(message: &LdapMessage) -> bool { + match message.protocol_op { + ProtocolOp::BindRequest(_) + | ProtocolOp::UnbindRequest + | ProtocolOp::SearchRequest(_) + | ProtocolOp::ModifyRequest(_) + | ProtocolOp::AddRequest(_) + | ProtocolOp::DelRequest(_) + | ProtocolOp::ModDnRequest(_) + | ProtocolOp::CompareRequest(_) + | ProtocolOp::AbandonRequest(_) + | ProtocolOp::ExtendedRequest(_) => { + return true; } - } -} - -impl Display for ProtocolOp { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ProtocolOp::BindRequest(_) => write!(f, "bind_request"), - ProtocolOp::BindResponse(_) => write!(f, "bind_response"), - ProtocolOp::UnbindRequest => write!(f, "unbind_request"), - ProtocolOp::SearchRequest(_) => write!(f, "search_request"), - ProtocolOp::SearchResultEntry(_) => write!(f, "search_result_entry"), - ProtocolOp::SearchResultDone(_) => write!(f, "search_result_done"), - ProtocolOp::SearchResultReference(_) => write!(f, "search_result_reference"), - ProtocolOp::ModifyRequest(_) => write!(f, "modify_request"), - ProtocolOp::ModifyResponse(_) => write!(f, "modify_response"), - ProtocolOp::AddRequest(_) => write!(f, "add_request"), - ProtocolOp::AddResponse(_) => write!(f, "add_response"), - ProtocolOp::DelRequest(_) => write!(f, "del_request"), - ProtocolOp::DelResponse(_) => write!(f, "del_response"), - ProtocolOp::ModDnRequest(_) => write!(f, "mod_dn_request"), - ProtocolOp::ModDnResponse(_) => write!(f, "mod_dn_response"), - ProtocolOp::CompareRequest(_) => write!(f, "compare_request"), - ProtocolOp::CompareResponse(_) => write!(f, "compare_response"), - ProtocolOp::AbandonRequest(_) => write!(f, "abandon_request"), - ProtocolOp::ExtendedRequest(_) => write!(f, "extended_request"), - ProtocolOp::ExtendedResponse(_) => write!(f, "extended_response"), - ProtocolOp::IntermediateResponse(_) => write!(f, "intermediate_response"), + _ => { + return false; } } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct LdapMessage { - pub message_id: MessageID, - pub protocol_op: ProtocolOp, - pub controls: Option>, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Control { - pub control_type: LdapOID, - pub criticality: bool, - pub control_value: Option>, -} - -impl From> for LdapMessage { - fn from(ldap_msg: ldap_parser::ldap::LdapMessage) -> Self { - let message_id = MessageID(ldap_msg.message_id.0); - let protocol_op = match ldap_msg.protocol_op { - ldap_parser::ldap::ProtocolOp::BindRequest(msg) => Self::from_bind_request(msg), - ldap_parser::ldap::ProtocolOp::BindResponse(msg) => Self::from_bind_response(msg), - ldap_parser::ldap::ProtocolOp::UnbindRequest => ProtocolOp::UnbindRequest, - ldap_parser::ldap::ProtocolOp::SearchRequest(msg) => Self::from_search_request(msg), - ldap_parser::ldap::ProtocolOp::SearchResultEntry(msg) => { - Self::from_search_result_entry(msg) - } - ldap_parser::ldap::ProtocolOp::SearchResultDone(msg) => { - Self::from_search_result_done(msg) - } - ldap_parser::ldap::ProtocolOp::SearchResultReference(msg) => { - Self::from_search_result_reference(msg) - } - ldap_parser::ldap::ProtocolOp::ModifyRequest(msg) => Self::from_modify_request(msg), - ldap_parser::ldap::ProtocolOp::ModifyResponse(msg) => Self::from_modify_response(msg), - ldap_parser::ldap::ProtocolOp::AddRequest(msg) => Self::from_add_request(msg), - ldap_parser::ldap::ProtocolOp::AddResponse(msg) => Self::from_add_response(msg), - ldap_parser::ldap::ProtocolOp::DelRequest(msg) => Self::from_del_request(msg), - ldap_parser::ldap::ProtocolOp::DelResponse(msg) => Self::from_del_response(msg), - ldap_parser::ldap::ProtocolOp::ModDnRequest(msg) => Self::from_mod_dn_request(msg), - ldap_parser::ldap::ProtocolOp::ModDnResponse(msg) => Self::from_mod_dn_response(msg), - ldap_parser::ldap::ProtocolOp::CompareRequest(msg) => Self::from_compare_request(msg), - ldap_parser::ldap::ProtocolOp::CompareResponse(msg) => Self::from_compare_response(msg), - ldap_parser::ldap::ProtocolOp::ExtendedRequest(msg) => Self::from_extended_request(msg), - ldap_parser::ldap::ProtocolOp::ExtendedResponse(msg) => { - Self::from_extended_response(msg) - } - ldap_parser::ldap::ProtocolOp::IntermediateResponse(msg) => { - Self::from_intermediate_response(msg) - } - ldap_parser::ldap::ProtocolOp::AbandonRequest(msg) => Self::from_abandon_request(msg), - }; - let controls = ldap_msg.controls.map(|ctls| { - ctls.iter() - .map(|ctl| Control { - control_type: LdapOID(ctl.control_type.0.to_string()), - criticality: ctl.criticality, - control_value: ctl.control_value.as_ref().map(|val| val.to_vec()), - }) - .collect() - }); - - Self { - message_id, - protocol_op, - controls, - } - } -} - -impl LdapMessage { - pub fn is_request(&self) -> bool { - match self.protocol_op { - ProtocolOp::BindRequest(_) - | ProtocolOp::UnbindRequest - | ProtocolOp::SearchRequest(_) - | ProtocolOp::ModifyRequest(_) - | ProtocolOp::AddRequest(_) - | ProtocolOp::DelRequest(_) - | ProtocolOp::ModDnRequest(_) - | ProtocolOp::CompareRequest(_) - | ProtocolOp::AbandonRequest(_) - | ProtocolOp::ExtendedRequest(_) => { - return true; - } - _ => { - return false; - } - } - } - - pub fn is_response(&self) -> bool { - // it is either a response or a request - return !self.is_request(); - } - - fn from_bind_request(msg: ldap_parser::ldap::BindRequest) -> ProtocolOp { - let authentication = match msg.authentication { - ldap_parser::ldap::AuthenticationChoice::Simple(val) => { - AuthenticationChoice::Simple(val.to_vec()) - } - ldap_parser::ldap::AuthenticationChoice::Sasl(val) => { - AuthenticationChoice::Sasl(SaslCredentials { - mechanism: LdapString(val.mechanism.0.to_string()), - credentials: val.credentials.map(|creds| creds.to_vec()), - }) - } - }; - ProtocolOp::BindRequest(BindRequest { - version: msg.version, - name: LdapDN(msg.name.0.to_string()), - authentication, - }) - } - - fn from_bind_response(msg: ldap_parser::ldap::BindResponse) -> ProtocolOp { - ProtocolOp::BindResponse(BindResponse { - result: LdapResult { - result_code: ResultCode(msg.result.result_code.0), - matched_dn: LdapDN(msg.result.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.result.diagnostic_message.0.to_string()), - }, - server_sasl_creds: msg - .server_sasl_creds - .map(|server_sasl_creds| server_sasl_creds.to_vec()), - }) - } - - fn from_search_request(msg: ldap_parser::ldap::SearchRequest) -> ProtocolOp { - let attributes = msg - .attributes - .iter() - .map(|s| LdapString(s.0.to_string())) - .collect(); - ProtocolOp::SearchRequest(SearchRequest { - base_object: LdapDN(msg.base_object.0.to_string()), - scope: SearchScope(msg.scope.0), - deref_aliases: DerefAliases(msg.deref_aliases.0), - size_limit: msg.size_limit, - time_limit: msg.time_limit, - types_only: msg.types_only, - filter: Filter::from(msg.filter), - attributes, - }) - } - - fn from_search_result_entry(msg: ldap_parser::ldap::SearchResultEntry) -> ProtocolOp { - let attributes = msg.attributes.iter().map(PartialAttribute::from).collect(); - ProtocolOp::SearchResultEntry(SearchResultEntry { - object_name: LdapDN(msg.object_name.0.to_string()), - attributes, - }) - } - - fn from_search_result_done(msg: ldap_parser::ldap::LdapResult) -> ProtocolOp { - ProtocolOp::SearchResultDone(LdapResult { - result_code: ResultCode(msg.result_code.0), - matched_dn: LdapDN(msg.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.diagnostic_message.0.to_string()), - }) - } - - fn from_search_result_reference(msg: Vec>) -> ProtocolOp { - let strs = msg.iter().map(|s| LdapString(s.0.to_string())).collect(); - ProtocolOp::SearchResultReference(strs) - } - - fn from_modify_request(msg: ldap_parser::ldap::ModifyRequest) -> ProtocolOp { - let changes = msg - .changes - .iter() - .map(|c| Change { - operation: Operation(c.operation.0), - modification: PartialAttribute::from(&c.modification), - }) - .collect(); - ProtocolOp::ModifyRequest(ModifyRequest { - object: LdapDN(msg.object.0.to_string()), - changes, - }) - } - - fn from_modify_response(msg: ldap_parser::ldap::ModifyResponse) -> ProtocolOp { - ProtocolOp::ModifyResponse(ModifyResponse { - result: LdapResult { - result_code: ResultCode(msg.result.result_code.0), - matched_dn: LdapDN(msg.result.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.result.diagnostic_message.0.to_string()), - }, - }) - } - - fn from_add_request(msg: ldap_parser::ldap::AddRequest) -> ProtocolOp { - let attributes = msg.attributes.iter().map(Attribute::from).collect(); - ProtocolOp::AddRequest(AddRequest { - entry: LdapDN(msg.entry.0.to_string()), - attributes, - }) - } - - fn from_add_response(msg: ldap_parser::ldap::LdapResult) -> ProtocolOp { - ProtocolOp::AddResponse(LdapResult { - result_code: ResultCode(msg.result_code.0), - matched_dn: LdapDN(msg.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.diagnostic_message.0.to_string()), - }) - } - - fn from_del_request(msg: ldap_parser::ldap::LdapDN<'_>) -> ProtocolOp { - ProtocolOp::DelRequest(LdapDN(msg.0.to_string())) - } - - fn from_del_response(msg: ldap_parser::ldap::LdapResult) -> ProtocolOp { - ProtocolOp::DelResponse(LdapResult { - result_code: ResultCode(msg.result_code.0), - matched_dn: LdapDN(msg.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.diagnostic_message.0.to_string()), - }) - } - - fn from_mod_dn_request(msg: ldap_parser::ldap::ModDnRequest) -> ProtocolOp { - ProtocolOp::ModDnRequest(ModDnRequest { - entry: LdapDN(msg.entry.0.to_string()), - newrdn: RelativeLdapDN(msg.newrdn.0.to_string()), - deleteoldrdn: msg.deleteoldrdn, - newsuperior: if let Some(newsuperior) = msg.newsuperior { - Some(LdapDN(newsuperior.0.to_string())) - } else { - None - }, - }) - } - - fn from_mod_dn_response(msg: ldap_parser::ldap::LdapResult) -> ProtocolOp { - ProtocolOp::ModDnResponse(LdapResult { - result_code: ResultCode(msg.result_code.0), - matched_dn: LdapDN(msg.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.diagnostic_message.0.to_string()), - }) - } - - fn from_compare_request(msg: ldap_parser::ldap::CompareRequest) -> ProtocolOp { - ProtocolOp::CompareRequest(CompareRequest { - entry: LdapDN(msg.entry.0.to_string()), - ava: AttributeValueAssertion::from(&msg.ava), - }) - } - - fn from_compare_response(msg: ldap_parser::ldap::LdapResult) -> ProtocolOp { - ProtocolOp::CompareResponse(LdapResult { - result_code: ResultCode(msg.result_code.0), - matched_dn: LdapDN(msg.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.diagnostic_message.0.to_string()), - }) - } - - fn from_abandon_request(msg: ldap_parser::ldap::MessageID) -> ProtocolOp { - ProtocolOp::AbandonRequest(AbandonRequest { message_id: msg.0 }) - } - - fn from_extended_request(msg: ldap_parser::ldap::ExtendedRequest) -> ProtocolOp { - ProtocolOp::ExtendedRequest(ExtendedRequest { - request_name: LdapOID(msg.request_name.0.to_string()), - request_value: msg - .request_value - .map(|request_value| request_value.to_vec()), - }) - } - - fn from_extended_response(msg: ldap_parser::ldap::ExtendedResponse) -> ProtocolOp { - ProtocolOp::ExtendedResponse(ExtendedResponse { - result: LdapResult { - result_code: ResultCode(msg.result.result_code.0), - matched_dn: LdapDN(msg.result.matched_dn.0.to_string()), - diagnostic_message: LdapString(msg.result.diagnostic_message.0.to_string()), - }, - response_name: msg - .response_name - .map(|response_name| LdapOID(response_name.0.to_string())), - response_value: msg - .response_value - .map(|response_value| response_value.to_vec()), - }) - } - - fn from_intermediate_response(msg: ldap_parser::ldap::IntermediateResponse) -> ProtocolOp { - ProtocolOp::IntermediateResponse(IntermediateResponse { - response_name: msg - .response_name - .map(|response_name| LdapOID(response_name.0.to_string())), - response_value: msg - .response_value - .map(|response_value| response_value.to_vec()), - }) - } +pub fn ldap_is_response(message: &LdapMessage) -> bool { + // it is either a response or a request + return !ldap_is_request(message); } -pub fn ldap_parse_msg(input: &[u8]) -> ParseResult<'_, ldap_parser::ldap::LdapMessage<'_>, LdapError> { - ldap_parser::ldap::LdapMessage::from_ber(input) +pub fn ldap_parse_msg(input: &[u8]) -> ParseResult { + LdapMessage::from_ber(input) }