ldap: factorize code and remove duplicated structs, use ldap_parser where relevant

pull/13432/head
Pierre Chifflier 1 year ago committed by Victor Julien
parent c152c5c7e0
commit bda22c1f4a

@ -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<u8>);
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::<LdapMessage, u8>(
&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,
};

@ -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 <giuseppe@glongo.it>
use crate::ldap::types::LdapString;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Filter {
And(Vec<Filter>),
Or(Vec<Filter>),
Not(Box<Filter>),
EqualityMatch(AttributeValueAssertion),
Substrings(SubstringFilter),
GreaterOrEqual(AttributeValueAssertion),
LessOrEqual(AttributeValueAssertion),
Present(LdapString),
ApproxMatch(AttributeValueAssertion),
ExtensibleMatch(MatchingRuleAssertion),
}
impl From<ldap_parser::filter::Filter<'_>> 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<Substring> = 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<AttributeValue>,
}
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<AttributeValue> = 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<AttributeValue>,
}
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<AttributeValue> = 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<u8>,
}
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<LdapString>,
pub rule_type: Option<AttributeDescription>,
pub assertion_value: AssertionValue,
pub dn_attributes: Option<bool>,
}
#[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<Substring>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Substring {
Initial(AssertionValue),
Any(AssertionValue),
Final(AssertionValue),
}
impl From<ldap_parser::filter::Substring<'_>> 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<u8>);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttributeValue(pub Vec<u8>);

@ -15,7 +15,8 @@
* 02110-1301, USA.
*/
// written by Giuseppe Longo <giuseppe@glongo.it>
// Author: Giuseppe Longo <giuseppe@glongo.it>
// Author: Pierre Chifflier <chifflier@wzdftpd.net>
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<LdapMessage>,
pub responses: VecDeque<LdapMessage>,
pub request: Option<LdapMessage<'static>>,
pub responses: VecDeque<LdapMessage<'static>>,
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();
}

@ -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() {

@ -18,7 +18,6 @@
// written by Giuseppe Longo <giuseppe@glongo.it>
pub mod detect;
pub mod filters;
pub mod ldap;
pub mod logger;
pub mod types;

@ -15,188 +15,12 @@
* 02110-1301, USA.
*/
// written by Giuseppe Longo <giuseppe@glongo.it>
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
// Author: Giuseppe Longo <giuseppe@glongo.it>
// Author: Pierre Chifflier <chifflier@wzdftpd.net>
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<Vec<u8>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AuthenticationChoice {
Simple(Vec<u8>),
Sasl(SaslCredentials),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BindResponse {
pub result: LdapResult,
pub server_sasl_creds: Option<Vec<u8>>,
}
#[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<LdapString>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SearchResultEntry {
pub object_name: LdapDN,
pub attributes: Vec<PartialAttribute>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ModifyRequest {
pub object: LdapDN,
pub changes: Vec<Change>,
}
#[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<Attribute>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ModDnRequest {
pub entry: LdapDN,
pub newrdn: RelativeLdapDN,
pub deleteoldrdn: bool,
pub newsuperior: Option<LdapDN>,
}
#[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<Vec<u8>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExtendedResponse {
pub result: LdapResult,
pub response_name: Option<LdapOID>,
pub response_value: Option<Vec<u8>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IntermediateResponse {
pub response_name: Option<LdapOID>,
pub response_value: Option<Vec<u8>>,
}
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<LdapString>),
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<Vec<Control>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Control {
pub control_type: LdapOID,
pub criticality: bool,
pub control_value: Option<Vec<u8>>,
}
impl From<ldap_parser::ldap::LdapMessage<'_>> 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<ldap_parser::ldap::LdapString<'_>>) -> 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, LdapError> {
LdapMessage::from_ber(input)
}

Loading…
Cancel
Save