use lber::common::TagClass;
use lber::structure::{StructureTag, PL};
use lber::structures::ASNTag;
use lber::structures::{
Boolean, Enumerated, ExplicitTag, Integer, Null, OctetString, Sequence, Set, Tag,
};
use lber::universal::Types;
use lber::write as lber_write;
use lber::parse::Parser;
use bytes::BytesMut;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;
use crate::control::LdapControl;
use crate::error::LdapProtoError;
use std::convert::{From, TryFrom};
use std::hash::Hash;
use std::iter::{once, once_with};
pub const OID_WHOAMI: &str = "1.3.6.1.4.1.4203.1.11.3";
pub const OID_PASSWORD_MODIFY: &str = "1.3.6.1.4.1.4203.1.11.1";
#[macro_export]
macro_rules! bytes_to_string {
($bytes:expr) => {
if let Ok(s) = String::from_utf8($bytes.clone()) {
s
} else {
use base64::Engine;
let mut s = format!(
"b64[{}]",
base64::engine::general_purpose::URL_SAFE.encode(&$bytes)
);
if s.len() > 100 {
s.truncate(96);
s.push_str("...]");
}
s
}
};
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapMsg {
pub msgid: i32,
pub op: LdapOp,
pub ctrl: Vec<LdapControl>,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(i64)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum SyncRequestMode {
RefreshOnly = 1,
RefreshAndPersist = 3,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(i64)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum SyncStateValue {
Present = 0,
Add = 1,
Modify = 2,
Delete = 3,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(i64)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapResultCode {
Success = 0,
OperationsError = 1,
ProtocolError = 2,
TimeLimitExceeded = 3,
SizeLimitExceeded = 4,
CompareFalse = 5,
CompareTrue = 6,
AuthMethodNotSupported = 7,
StrongerAuthRequired = 8,
Referral = 10,
AdminLimitExceeded = 11,
UnavailableCriticalExtension = 12,
ConfidentialityRequired = 13,
SaslBindInProgress = 14,
NoSuchAttribute = 16,
UndefinedAttributeType = 17,
InappropriateMatching = 18,
ConstraintViolation = 19,
AttributeOrValueExists = 20,
InvalidAttributeSyntax = 21,
NoSuchObject = 32,
AliasProblem = 33,
InvalidDNSyntax = 34,
AliasDereferencingProblem = 36,
InappropriateAuthentication = 48,
InvalidCredentials = 49,
InsufficentAccessRights = 50,
Busy = 51,
Unavailable = 52,
UnwillingToPerform = 53,
LoopDetect = 54,
NamingViolation = 64,
ObjectClassViolation = 65,
NotAllowedOnNonLeaf = 66,
NotALlowedOnRDN = 67,
EntryAlreadyExists = 68,
ObjectClassModsProhibited = 69,
AffectsMultipleDSAs = 71,
Other = 80,
EsyncRefreshRequired = 4096,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapResult {
pub code: LdapResultCode,
pub matcheddn: String,
pub message: String,
pub referral: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapOp {
BindRequest(LdapBindRequest),
BindResponse(LdapBindResponse),
UnbindRequest,
SearchRequest(LdapSearchRequest),
SearchResultEntry(LdapSearchResultEntry),
SearchResultDone(LdapResult),
SearchResultReference(LdapSearchResultReference),
ModifyRequest(LdapModifyRequest),
ModifyResponse(LdapResult),
AddRequest(LdapAddRequest),
AddResponse(LdapResult),
DelRequest(String),
DelResponse(LdapResult),
ModifyDNRequest(LdapModifyDNRequest),
ModifyDNResponse(LdapResult),
CompareRequest(LdapCompareRequest),
CompareResult(LdapResult),
AbandonRequest(i32),
ExtendedRequest(LdapExtendedRequest),
ExtendedResponse(LdapExtendedResponse),
IntermediateResponse(LdapIntermediateResponse),
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapBindCred {
Simple(String),
SASL(SaslCredentials),
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename = "snake_case"))]
pub struct SaslCredentials {
pub mechanism: String,
pub credentials: Vec<u8>,
}
impl From<SaslCredentials> for StructureTag {
fn from(value: SaslCredentials) -> Self {
StructureTag {
id: 3, class: TagClass::Context, payload: PL::C(vec![
StructureTag {
class: TagClass::Universal,
id: Types::OctetString as u64, payload: PL::P(value.mechanism.into_bytes()),
},
StructureTag {
class: TagClass::Universal,
id: Types::OctetString as u64,
payload: PL::P(value.credentials),
},
]),
}
}
}
impl fmt::Debug for SaslCredentials {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SaslCredentials")
.field("mechanism", &self.mechanism)
.finish()
}
}
impl fmt::Debug for LdapBindCred {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LdapBindCred::Simple(_) => f.debug_struct("LdapBindCred::Simple").finish(),
LdapBindCred::SASL(_) => f.debug_struct("LdapBindCred::SASL").finish(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapBindRequest {
pub dn: String,
pub cred: LdapBindCred,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapBindResponse {
pub res: LdapResult,
pub saslcreds: Option<Vec<u8>>,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[repr(i64)]
pub enum LdapSearchScope {
Base = 0,
OneLevel = 1,
Subtree = 2,
Children = 3,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(i64)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapDerefAliases {
Never = 0,
InSearching = 1,
FindingBaseObj = 2,
Always = 3,
}
#[derive(Debug, Clone, PartialEq, Default, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapSubstringFilter {
pub initial: Option<String>,
pub any: Vec<String>,
pub final_: Option<String>, }
impl From<&str> for LdapSubstringFilter {
fn from(value: &str) -> Self {
let mut filter = LdapSubstringFilter {
initial: None,
any: Vec::new(),
final_: None,
};
let mut split_iter = value.split('*');
if let Some(start) = split_iter.next() {
if !start.is_empty() {
filter.initial = Some(start.to_string());
}
}
if let Some(end) = split_iter.next_back() {
if !end.is_empty() {
filter.final_ = Some(end.to_string());
}
}
split_iter.for_each(|v| filter.any.push(v.to_string()));
filter
}
}
impl From<String> for LdapSubstringFilter {
fn from(value: String) -> Self {
Self::from(value.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Default, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapMatchingRuleAssertion {
pub matching_rule: Option<String>,
pub type_: Option<String>,
pub match_value: String,
pub dn_attributes: bool, }
impl LdapMatchingRuleAssertion {
pub(crate) fn from_strings(left: &str, right: String) -> Self {
let mut split = left.split(':');
let type_ = split.next().and_then(|first| {
if first.is_empty() {
None
} else {
Some(first.to_string())
}
});
let (matching_rule, dn_attributes) = split
.next()
.map(|part| match part {
"dn" => (None, true),
_ => (Some(part.to_string()), false),
})
.unwrap_or((None, false));
let matching_rule = split
.next()
.map_or(matching_rule, |oid| Some(oid.to_string()));
Self {
matching_rule,
type_,
match_value: right,
dn_attributes,
}
}
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapFilter {
And(Vec<LdapFilter>),
Or(Vec<LdapFilter>),
Not(Box<LdapFilter>),
Equality(String, String),
Substring(String, LdapSubstringFilter),
GreaterOrEqual(String, String),
LessOrEqual(String, String),
Present(String),
Approx(String, String),
Extensible(LdapMatchingRuleAssertion),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapSearchRequest {
pub base: String,
pub scope: LdapSearchScope,
pub aliases: LdapDerefAliases,
pub sizelimit: i32,
pub timelimit: i32,
pub typesonly: bool,
pub filter: LdapFilter,
pub attrs: Vec<String>,
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapPartialAttribute {
pub atype: String,
pub vals: Vec<Vec<u8>>,
}
impl LdapPartialAttribute {
pub fn size(&self) -> usize {
std::mem::size_of::<Self>()
+ self.atype.capacity()
+ (self.vals.capacity() * std::mem::size_of::<Vec<()>>())
+ self
.vals
.iter()
.map(|val| val.capacity() * std::mem::size_of::<Vec<()>>())
.sum::<usize>()
}
}
impl fmt::Debug for LdapPartialAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("LdapPartialAttribute");
f.field("atype", &self.atype);
let atype_lower = self.atype.to_lowercase();
if atype_lower == "userpassword"
|| atype_lower == "ipanthash"
|| atype_lower == "oathtotptoken"
|| atype_lower == "oathhotptoken"
{
f.field("vals", &["********"]);
} else {
let d_vals: Vec<_> = self.vals.iter().map(|val| bytes_to_string!(val)).collect();
f.field("vals", &d_vals);
}
f.finish()
}
}
pub type LdapAttribute = LdapPartialAttribute;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapSearchResultEntry {
pub dn: String,
pub attributes: Vec<LdapPartialAttribute>,
}
impl LdapSearchResultEntry {
pub fn size(&self) -> usize {
std::mem::size_of::<Self>()
+ self.dn.capacity()
+ self
.attributes
.iter()
.map(|attr| attr.size())
.sum::<usize>()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapAddRequest {
pub dn: String,
pub attributes: Vec<LdapAttribute>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapModifyRequest {
pub dn: String,
pub changes: Vec<LdapModify>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapModify {
pub operation: LdapModifyType,
pub modification: LdapPartialAttribute,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(i64)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapModifyType {
Add = 0,
Delete = 1,
Replace = 2,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapModifyDNRequest {
pub dn: String,
pub newrdn: String,
pub deleteoldrdn: bool,
pub new_superior: Option<String>,
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapCompareRequest {
pub dn: String,
pub atype: String,
pub val: Vec<u8>,
}
impl fmt::Debug for LdapCompareRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let d_val = bytes_to_string!(self.val);
f.debug_struct("LdapCompareRequest")
.field("dn", &self.dn)
.field("atype", &self.atype)
.field("val", &d_val)
.finish()
}
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapExtendedRequest {
pub name: String,
pub value: Option<Vec<u8>>,
}
impl fmt::Debug for LdapExtendedRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("LdapExtendedRequest");
f.field("name", &self.name);
if self.name == OID_PASSWORD_MODIFY {
f.field("value", &self.value.as_ref().map(|_| "vec![...]"));
} else {
let d_value = self.value.as_ref().map(|s| bytes_to_string!(s));
f.field("value", &d_value);
}
f.finish()
}
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapExtendedResponse {
pub res: LdapResult,
pub name: Option<String>,
pub value: Option<Vec<u8>>,
}
impl fmt::Debug for LdapExtendedResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LdapExtendedResponse")
.field("result", &self.res)
.field("name", &self.name)
.field("value", &"vec![...]")
.finish()
}
}
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum LdapIntermediateResponse {
SyncInfoNewCookie {
cookie: Vec<u8>,
},
SyncInfoRefreshDelete {
cookie: Option<Vec<u8>>,
done: bool,
},
SyncInfoRefreshPresent {
cookie: Option<Vec<u8>>,
done: bool,
},
SyncInfoIdSet {
cookie: Option<Vec<u8>>,
refresh_deletes: bool,
syncuuids: Vec<Uuid>,
},
Raw {
name: Option<String>,
value: Option<Vec<u8>>,
},
}
impl fmt::Debug for LdapIntermediateResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
LdapIntermediateResponse::SyncInfoNewCookie { cookie } => {
let d_cookie = bytes_to_string!(cookie);
f.debug_struct("LdapIntermediateResponse::SyncInfoNewCookie")
.field("cookie", &d_cookie)
.finish()
}
LdapIntermediateResponse::SyncInfoRefreshDelete { cookie, done } => {
let d_cookie = cookie.as_ref().map(|s| bytes_to_string!(s));
f.debug_struct("LdapIntermediateResponse::SyncInfoRefreshDelete")
.field("cookie", &d_cookie)
.field("done", &done)
.finish()
}
LdapIntermediateResponse::SyncInfoRefreshPresent { cookie, done } => {
let d_cookie = cookie.as_ref().map(|s| bytes_to_string!(s));
f.debug_struct("LdapIntermediateResponse::SyncInfoRefreshPresent")
.field("cookie", &d_cookie)
.field("done", &done)
.finish()
}
LdapIntermediateResponse::SyncInfoIdSet {
cookie,
refresh_deletes,
syncuuids,
} => {
let d_cookie = cookie.as_ref().map(|s| bytes_to_string!(s));
f.debug_struct("LdapIntermediateResponse::SyncInfoIdSet")
.field("cookie", &d_cookie)
.field("refresh_deletes", &refresh_deletes)
.field("syncuuids", &syncuuids)
.finish()
}
LdapIntermediateResponse::Raw { name, value } => {
let d_value = value.as_ref().map(|s| bytes_to_string!(s));
f.debug_struct("LdapIntermediateResponse::Raw")
.field("name", &name)
.field("value", &d_value)
.finish()
}
}
}
}
#[derive(Clone, PartialEq)]
pub struct LdapWhoamiRequest {}
impl From<LdapWhoamiRequest> for LdapExtendedRequest {
fn from(_value: LdapWhoamiRequest) -> LdapExtendedRequest {
LdapExtendedRequest {
name: OID_WHOAMI.to_string(),
value: None,
}
}
}
#[derive(Clone, PartialEq)]
pub struct LdapWhoamiResponse {
pub dn: Option<String>,
}
impl TryFrom<&LdapExtendedResponse> for LdapWhoamiResponse {
type Error = LdapProtoError;
fn try_from(value: &LdapExtendedResponse) -> Result<Self, Self::Error> {
if value.name.is_some() {
return Err(LdapProtoError::WhoamiResponseName);
}
let dn = value
.value
.as_ref()
.and_then(|bv| String::from_utf8(bv.to_vec()).ok());
Ok(LdapWhoamiResponse { dn })
}
}
#[derive(Clone, PartialEq)]
pub struct LdapPasswordModifyRequest {
pub user_identity: Option<String>,
pub old_password: Option<String>,
pub new_password: Option<String>,
}
impl fmt::Debug for LdapPasswordModifyRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("LdapPasswordModifyRequest");
f.field("user_identity", &self.user_identity);
f.field(
"old_password",
&self.old_password.as_ref().map(|_| "********"),
);
f.field(
"new_password",
&self.old_password.as_ref().map(|_| "********"),
);
f.finish()
}
}
#[derive(Clone, PartialEq)]
pub struct LdapPasswordModifyResponse {
pub res: LdapResult,
pub gen_password: Option<String>,
}
impl fmt::Debug for LdapPasswordModifyResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LdapPasswordModifyResponse")
.field("result", &self.res)
.field("gen_password", &self.gen_password.is_some())
.finish()
}
}
impl From<LdapPasswordModifyRequest> for LdapExtendedRequest {
fn from(value: LdapPasswordModifyRequest) -> LdapExtendedRequest {
let inner: Vec<_> = vec![
value.user_identity.map(|s| {
Tag::OctetString(OctetString {
class: TagClass::Context,
id: 0,
inner: Vec::from(s),
})
}),
value.old_password.map(|s| {
Tag::OctetString(OctetString {
class: TagClass::Context,
id: 1,
inner: Vec::from(s),
})
}),
value.new_password.map(|s| {
Tag::OctetString(OctetString {
class: TagClass::Context,
id: 2,
inner: Vec::from(s),
})
}),
];
let tag = Tag::Sequence(Sequence {
inner: inner.into_iter().flatten().collect(),
..Default::default()
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
LdapExtendedRequest {
name: OID_PASSWORD_MODIFY.to_string(),
value: Some(bytes.to_vec()),
}
}
}
impl TryFrom<&LdapExtendedRequest> for LdapPasswordModifyRequest {
type Error = LdapProtoError;
fn try_from(value: &LdapExtendedRequest) -> Result<Self, Self::Error> {
if value.name != OID_PASSWORD_MODIFY {
return Err(LdapProtoError::PasswordModifyRequestOid);
}
let buf = if let Some(b) = &value.value {
b
} else {
return Err(LdapProtoError::PasswordModifyRequestEmpty);
};
let mut parser = Parser::new();
let (_rem, msg) = parser
.parse(buf)
.map_err(|_| LdapProtoError::PasswordModifyRequestBer)?;
let seq = msg
.match_id(Types::Sequence as u64)
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::PasswordModifyRequestBer)?;
let mut lpmr = LdapPasswordModifyRequest {
user_identity: None,
old_password: None,
new_password: None,
};
for t in seq.into_iter() {
let id = t.id;
let s = t
.expect_primitive()
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::PasswordModifyRequestBer)?;
match id {
0 => lpmr.user_identity = Some(s),
1 => lpmr.old_password = Some(s),
2 => lpmr.new_password = Some(s),
_ => return Err(LdapProtoError::PasswordModifyRequestValueId),
}
}
Ok(lpmr)
}
}
impl From<LdapPasswordModifyResponse> for LdapExtendedResponse {
fn from(value: LdapPasswordModifyResponse) -> LdapExtendedResponse {
let inner: Vec<_> = vec![value.gen_password.map(|s| {
Tag::OctetString(OctetString {
class: TagClass::Context,
id: 0,
inner: Vec::from(s),
})
})];
let tag = Tag::Sequence(Sequence {
inner: inner.into_iter().flatten().collect(),
..Default::default()
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
LdapExtendedResponse {
res: value.res,
name: None,
value: Some(bytes.to_vec()),
}
}
}
impl TryFrom<&LdapExtendedResponse> for LdapPasswordModifyResponse {
type Error = LdapProtoError;
fn try_from(value: &LdapExtendedResponse) -> Result<Self, Self::Error> {
if value.name.is_some() {
return Err(LdapProtoError::PasswordModifyResponseName);
}
let buf = if let Some(b) = &value.value {
b
} else {
return Err(LdapProtoError::PasswordModifyResponseEmpty);
};
let mut parser = Parser::new();
let (_rem, msg) = parser
.parse(buf)
.map_err(|_| LdapProtoError::PasswordModifyResponseBer)?;
let mut seq = msg
.match_id(Types::Sequence as u64)
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::PasswordModifyResponseBer)?;
let gen_password = seq
.pop()
.and_then(|t| t.match_id(0))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok());
Ok(LdapPasswordModifyResponse {
res: value.res.clone(),
gen_password,
})
}
}
impl From<LdapBindCred> for Tag {
fn from(value: LdapBindCred) -> Tag {
match value {
LdapBindCred::Simple(pw) => Tag::OctetString(OctetString {
id: 0,
class: TagClass::Context,
inner: Vec::from(pw),
}),
LdapBindCred::SASL(token) => Tag::StructureTag(token.into()),
}
}
}
impl LdapMsg {
pub fn new(msgid: i32, op: LdapOp) -> Self {
LdapMsg {
msgid,
op,
ctrl: Vec::new(),
}
}
pub fn new_with_ctrls(msgid: i32, op: LdapOp, ctrl: Vec<LdapControl>) -> Self {
LdapMsg { msgid, op, ctrl }
}
pub fn try_from_openldap_mem_dump(bytes: &[u8]) -> Result<Self, LdapProtoError> {
let mut parser = lber::parse::Parser::new();
let (r1_bytes, msgid_tag) = parser
.parse(bytes)
.map_err(|_| LdapProtoError::OlMemDumpBer)?;
let (r2_bytes, op_tag) = parser
.parse(r1_bytes)
.map_err(|_| LdapProtoError::OlMemDumpBer)?;
let ctrl_tag = if r2_bytes.is_empty() {
None
} else {
parser
.parse(r2_bytes)
.map(|(_rem, tag)| Some(tag))
.map_err(|_| LdapProtoError::OlMemDumpBer)?
};
let msgid = msgid_tag
.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::Integer as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.map(|i| i as i32)
.ok_or(LdapProtoError::OlMemDumpBer)?;
let op = LdapOp::try_from(op_tag)?;
let ctrl = ctrl_tag
.and_then(|t| t.match_class(TagClass::Context))
.and_then(|t| t.match_id(0))
.map(|_t| Vec::new())
.unwrap_or_else(Vec::new);
Ok(LdapMsg { msgid, op, ctrl })
}
}
impl TryFrom<StructureTag> for LdapMsg {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
let mut seq = value
.match_id(Types::Sequence as u64)
.and_then(|t| t.expect_constructed())
.ok_or_else(|| {
error!("Message is not constructed");
LdapProtoError::LdapMsgBer
})?;
let (msgid_tag, op_tag, ctrl_tag) = match seq.len() {
2 => {
let c = None;
let o = seq.pop();
let m = seq.pop();
(m, o, c)
}
3 => {
let c = seq.pop();
let o = seq.pop();
let m = seq.pop();
(m, o, c)
}
_ => {
error!("Invalid ldapmsg sequence length");
return Err(LdapProtoError::LdapMsgSeqLen);
}
};
trace!(?msgid_tag, ?op_tag, ?ctrl_tag);
let msgid = msgid_tag
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Integer as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.map(|i| i as i32)
.ok_or_else(|| {
error!("Invalid msgid");
LdapProtoError::LdapMsgId
})?;
let op = op_tag.ok_or_else(|| {
error!("No ldap op present");
LdapProtoError::LdapMsgOp
})?;
let op = LdapOp::try_from(op)?;
let ctrl_vec = ctrl_tag
.and_then(|t| t.match_class(TagClass::Context))
.and_then(|t| t.match_id(0))
.and_then(|t| t.expect_constructed())
.unwrap_or_default();
let ctrl = ctrl_vec
.into_iter()
.map(TryInto::<LdapControl>::try_into)
.collect::<Result<Vec<_>, _>>()?;
let msg = LdapMsg { msgid, op, ctrl };
trace!(ldapmsg = ?msg);
Ok(msg)
}
}
impl From<LdapMsg> for StructureTag {
fn from(value: LdapMsg) -> StructureTag {
let LdapMsg { msgid, op, ctrl } = value;
let seq: Vec<_> = once_with(|| {
Some(Tag::Integer(Integer {
inner: msgid as i64,
..Default::default()
}))
})
.chain(once_with(|| Some(op.into())))
.chain(once_with(|| {
if ctrl.is_empty() {
None
} else {
let inner = ctrl.into_iter().map(|c| c.into()).collect();
Some(Tag::Sequence(Sequence {
class: TagClass::Context,
id: 0,
inner,
}))
}
}))
.chain(once(None))
.flatten()
.collect();
Tag::Sequence(Sequence {
inner: seq,
..Default::default()
})
.into_structure()
}
}
impl TryFrom<StructureTag> for LdapOp {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
let StructureTag { class, id, payload } = value;
if class != TagClass::Application {
error!("ldap op is not tagged as application");
return Err(LdapProtoError::LdapOpTag);
}
match (id, payload) {
(0, PL::C(inner)) => LdapBindRequest::try_from(inner).map(LdapOp::BindRequest),
(1, PL::C(inner)) => LdapBindResponse::try_from(inner).map(LdapOp::BindResponse),
(2, _) => Ok(LdapOp::UnbindRequest),
(3, PL::C(inner)) => LdapSearchRequest::try_from(inner).map(LdapOp::SearchRequest),
(4, PL::C(inner)) => {
LdapSearchResultEntry::try_from(inner).map(LdapOp::SearchResultEntry)
}
(5, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::SearchResultDone(lr))
}
(6, PL::C(inner)) => LdapModifyRequest::try_from(inner).map(LdapOp::ModifyRequest),
(7, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::ModifyResponse(lr))
}
(8, PL::C(inner)) => LdapAddRequest::try_from(inner).map(LdapOp::AddRequest),
(9, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::AddResponse(lr))
}
(10, PL::P(inner)) => String::from_utf8(inner)
.ok()
.ok_or(LdapProtoError::DelRequestBer)
.map(LdapOp::DelRequest),
(11, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::DelResponse(lr))
}
(12, PL::C(inner)) => LdapModifyDNRequest::try_from(inner).map(LdapOp::ModifyDNRequest),
(13, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::ModifyDNResponse(lr))
}
(14, PL::C(inner)) => LdapCompareRequest::try_from(inner).map(LdapOp::CompareRequest),
(15, PL::C(inner)) => {
LdapResult::try_from_tag(inner).map(|(lr, _)| LdapOp::CompareResult(lr))
}
(16, PL::P(inner)) => ber_integer_to_i64(inner)
.ok_or(LdapProtoError::AbandonRequestBer)
.map(|s| LdapOp::AbandonRequest(s as i32)),
(19, PL::C(inner)) => {
LdapSearchResultReference::try_from(inner).map(LdapOp::SearchResultReference)
}
(23, PL::C(inner)) => LdapExtendedRequest::try_from(inner).map(LdapOp::ExtendedRequest),
(24, PL::C(inner)) => {
LdapExtendedResponse::try_from(inner).map(LdapOp::ExtendedResponse)
}
(25, PL::C(inner)) => {
LdapIntermediateResponse::try_from(inner).map(LdapOp::IntermediateResponse)
}
(id, _) => {
println!("unknown op -> {:?}", id);
Err(LdapProtoError::LdapOpUnknown)
}
}
}
}
impl From<LdapOp> for Tag {
fn from(value: LdapOp) -> Tag {
match value {
LdapOp::BindRequest(lbr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 0,
inner: lbr.into(),
}),
LdapOp::BindResponse(lbr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 1,
inner: lbr.into(),
}),
LdapOp::UnbindRequest => Tag::Null(Null {
class: TagClass::Application,
id: 2,
inner: (),
}),
LdapOp::SearchRequest(sr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 3,
inner: sr.into(),
}),
LdapOp::SearchResultEntry(sre) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 4,
inner: sre.into(),
}),
LdapOp::SearchResultDone(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 5,
inner: lr.into(),
}),
LdapOp::SearchResultReference(urls) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 19,
inner: urls.into(),
}),
LdapOp::ModifyRequest(mr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 6,
inner: mr.into(),
}),
LdapOp::ModifyResponse(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 7,
inner: lr.into(),
}),
LdapOp::AddRequest(lar) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 8,
inner: lar.into(),
}),
LdapOp::AddResponse(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 9,
inner: lr.into(),
}),
LdapOp::DelRequest(s) => Tag::OctetString(OctetString {
class: TagClass::Application,
id: 10,
inner: Vec::from(s),
}),
LdapOp::DelResponse(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 11,
inner: lr.into(),
}),
LdapOp::ModifyDNRequest(mdr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 12,
inner: mdr.into(),
}),
LdapOp::ModifyDNResponse(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 13,
inner: lr.into(),
}),
LdapOp::CompareRequest(cr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 14,
inner: cr.into(),
}),
LdapOp::CompareResult(lr) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 15,
inner: lr.into(),
}),
LdapOp::AbandonRequest(id) => Tag::Integer(Integer {
class: TagClass::Application,
id: 16,
inner: id as i64,
}),
LdapOp::ExtendedRequest(ler) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 23,
inner: ler.into(),
}),
LdapOp::ExtendedResponse(ler) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 24,
inner: ler.into(),
}),
LdapOp::IntermediateResponse(lir) => Tag::Sequence(Sequence {
class: TagClass::Application,
id: 25,
inner: lir.into(),
}),
}
}
}
impl TryFrom<StructureTag> for LdapBindCred {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
if value.class != TagClass::Context {
return Err(LdapProtoError::BindCredBer);
}
match value.id {
0 => value
.expect_primitive()
.and_then(|bv| String::from_utf8(bv).ok())
.map(LdapBindCred::Simple)
.ok_or(LdapProtoError::BindCredBer),
_ => Err(LdapProtoError::BindCredId),
}
}
}
impl TryFrom<Vec<StructureTag>> for LdapBindRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let v = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Integer as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.ok_or(LdapProtoError::BindRequestVersion)?;
if v != 3 {
return Err(LdapProtoError::BindRequestVersion);
};
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::BindRequestBer)?;
let cred = value
.pop()
.and_then(|v| LdapBindCred::try_from(v).ok())
.ok_or(LdapProtoError::BindRequestBer)?;
Ok(LdapBindRequest { dn, cred })
}
}
impl From<LdapBindRequest> for Vec<Tag> {
fn from(value: LdapBindRequest) -> Vec<Tag> {
vec![
Tag::Integer(Integer {
inner: 3,
..Default::default()
}),
Tag::OctetString(OctetString {
inner: Vec::from(value.dn),
..Default::default()
}),
value.cred.into(),
]
}
}
impl LdapResult {
fn into_tag_iter(self) -> impl Iterator<Item = Option<Tag>> {
let LdapResult {
code,
matcheddn,
message,
referral,
} = self;
once_with(|| {
Some(Tag::Enumerated(Enumerated {
inner: code as i64,
..Default::default()
}))
})
.chain(once_with(|| {
Some(Tag::OctetString(OctetString {
inner: Vec::from(matcheddn),
..Default::default()
}))
}))
.chain(once_with(|| {
Some(Tag::OctetString(OctetString {
inner: Vec::from(message),
..Default::default()
}))
}))
.chain(once_with(move || {
if !referral.is_empty() {
let inner = referral
.iter()
.map(|s| {
Tag::OctetString(OctetString {
inner: Vec::from(s.clone()),
..Default::default()
})
})
.collect();
Some(Tag::Sequence(Sequence {
class: TagClass::Context,
id: 3,
inner,
}))
} else {
None
}
}))
}
}
impl From<LdapResult> for Vec<Tag> {
fn from(value: LdapResult) -> Vec<Tag> {
value.into_tag_iter().flatten().collect()
}
}
impl LdapResult {
fn try_from_tag(
mut value: Vec<StructureTag>,
) -> Result<(Self, Vec<StructureTag>), LdapProtoError> {
value.reverse();
let code = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Enumerated as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.ok_or(LdapProtoError::ResultBer)
.and_then(LdapResultCode::try_from)?;
let matcheddn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ResultBer)?;
let message = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ResultBer)?;
let (_referrals, other): (Vec<_>, Vec<_>) = value.into_iter().partition(|v| v.id == 3);
let referral = Vec::new();
Ok((
LdapResult {
code,
matcheddn,
message,
referral,
},
other,
))
}
}
impl LdapBindResponse {
pub fn new_success(msg: &str) -> Self {
LdapBindResponse {
res: LdapResult {
code: LdapResultCode::Success,
matcheddn: "".to_string(),
message: msg.to_string(),
referral: Vec::new(),
},
saslcreds: None,
}
}
pub fn new_invalidcredentials(dn: &str, msg: &str) -> Self {
LdapBindResponse {
res: LdapResult {
code: LdapResultCode::InvalidCredentials,
matcheddn: dn.to_string(),
message: msg.to_string(),
referral: Vec::new(),
},
saslcreds: None,
}
}
}
impl TryFrom<Vec<StructureTag>> for LdapBindResponse {
type Error = LdapProtoError;
fn try_from(value: Vec<StructureTag>) -> Result<Self, Self::Error> {
trace!(?value);
let (res, tags) = LdapResult::try_from_tag(value)?;
let saslcreds = tags
.first()
.map(|tag| {
debug!(?tag);
let vec = tag
.clone()
.match_class(TagClass::Context)
.and_then(|t| t.match_id(7))
.and_then(|t| t.expect_primitive());
vec.ok_or(LdapProtoError::BindCredBer)
})
.transpose()?;
Ok(LdapBindResponse { res, saslcreds })
}
}
impl From<LdapBindResponse> for Vec<Tag> {
fn from(value: LdapBindResponse) -> Vec<Tag> {
let LdapBindResponse { res, saslcreds } = value;
res.into_tag_iter()
.chain(once_with(|| {
saslcreds.map(|sc| {
Tag::OctetString(OctetString {
inner: sc,
..Default::default()
})
})
}))
.flatten()
.collect()
}
}
impl TryFrom<StructureTag> for LdapFilter {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
if value.class != TagClass::Context {
error!("Invalid tagclass");
return Err(LdapProtoError::FilterTag);
};
match value.id {
0 => {
let inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid and filter");
LdapProtoError::FilterBer
})?;
let vf: Result<Vec<_>, _> = inner.into_iter().map(LdapFilter::try_from).collect();
Ok(LdapFilter::And(vf?))
}
1 => {
let inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid or filter");
LdapProtoError::FilterBer
})?;
let vf: Result<Vec<_>, _> = inner.into_iter().map(LdapFilter::try_from).collect();
Ok(LdapFilter::Or(vf?))
}
2 => {
let inner = value
.expect_constructed()
.and_then(|mut i| i.pop())
.ok_or_else(|| {
trace!("invalid not filter");
LdapProtoError::FilterBer
})?;
let inner_filt = LdapFilter::try_from(inner)?;
Ok(LdapFilter::Not(Box::new(inner_filt)))
}
3 => {
let mut inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid eq filter");
LdapProtoError::FilterBer
})?;
inner.reverse();
let a = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid attribute in eq filter");
LdapProtoError::FilterBer
})?;
let v = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| {
if cfg!(feature = "strict") {
t.match_id(Types::OctetString as u64)
} else {
Some(t)
}
})
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid value in eq filter");
LdapProtoError::FilterBer
})?;
Ok(LdapFilter::Equality(a, v))
}
4 => {
let mut inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid sub filter");
LdapProtoError::FilterBer
})?;
inner.reverse();
let ty = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::FilterBer)?;
let f = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.and_then(|bv| {
let mut filter = LdapSubstringFilter::default();
for (
i,
StructureTag {
class: _,
id,
payload,
},
) in bv.iter().enumerate()
{
match (id, payload) {
(0, PL::P(s)) => {
if i == 0 {
filter.initial = Some(String::from_utf8(s.clone()).ok()?);
} else {
return None;
}
}
(1, PL::P(s)) => {
filter.any.push(String::from_utf8(s.clone()).ok()?);
}
(2, PL::P(s)) => {
if i == bv.len() - 1 {
filter.final_ = Some(String::from_utf8(s.clone()).ok()?);
} else {
return None;
}
}
_ => return None,
}
}
Some(filter)
})
.ok_or(LdapProtoError::FilterBer)?;
Ok(LdapFilter::Substring(ty, f))
}
5 => {
let mut inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid ge filter");
LdapProtoError::FilterBer
})?;
inner.reverse();
let a = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid attribute in ge filter");
LdapProtoError::FilterBer
})?;
let v = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| {
if cfg!(feature = "strict") {
t.match_id(Types::OctetString as u64)
} else {
Some(t)
}
})
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid value in ge filter");
LdapProtoError::FilterBer
})?;
Ok(LdapFilter::GreaterOrEqual(a, v))
}
6 => {
let mut inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid le filter");
LdapProtoError::FilterBer
})?;
inner.reverse();
let a = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid attribute in le filter");
LdapProtoError::FilterBer
})?;
let v = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| {
if cfg!(feature = "strict") {
t.match_id(Types::OctetString as u64)
} else {
Some(t)
}
})
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid value in le filter");
LdapProtoError::FilterBer
})?;
Ok(LdapFilter::LessOrEqual(a, v))
}
7 => {
let a = value
.expect_primitive()
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or_else(|| {
trace!("invalid pres filter");
LdapProtoError::FilterBer
})?;
Ok(LdapFilter::Present(a))
}
8 => {
let mut inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid approx filter");
LdapProtoError::FilterBer
})?;
inner.reverse();
let a = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid attribute in approx filter");
LdapProtoError::FilterBer
})?;
let v = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| {
if cfg!(feature = "strict") {
t.match_id(Types::OctetString as u64)
} else {
Some(t)
}
})
.and_then(|t| t.expect_primitive())
.and_then(|bv| {
String::from_utf8(bv)
.map_err(|e| {
trace!(?e);
})
.ok()
})
.ok_or_else(|| {
trace!("invalid value in approx filter");
LdapProtoError::FilterBer
})?;
Ok(LdapFilter::Approx(a, v))
}
9 => {
let inner = value.expect_constructed().ok_or_else(|| {
trace!("invalid extensible filter");
LdapProtoError::FilterBer
})?;
let mut filter = LdapMatchingRuleAssertion::default();
for StructureTag { class, id, payload } in inner.into_iter().take(4) {
match (class, id, payload) {
(TagClass::Context, 1, PL::P(s)) => {
filter.matching_rule = Some(String::from_utf8(s).map_err(|e| {
trace!(?e);
LdapProtoError::FilterBer
})?)
}
(TagClass::Context, 2, PL::P(s)) => {
filter.type_ = Some(String::from_utf8(s).map_err(|e| {
trace!(?e);
LdapProtoError::FilterBer
})?)
}
(TagClass::Context, 3, PL::P(s)) => {
filter.match_value = String::from_utf8(s).map_err(|e| {
trace!(?e);
LdapProtoError::FilterBer
})?
}
(TagClass::Context, 4, PL::P(s)) => {
filter.dn_attributes = ber_bool_to_bool(s).unwrap_or(false);
}
_ => {
trace!("invalid extensible filter");
return Err(LdapProtoError::FilterBer);
}
}
}
Ok(LdapFilter::Extensible(filter))
}
_ => {
trace!("invalid value tag");
Err(LdapProtoError::FilterBer)
}
}
}
}
impl From<LdapFilter> for Tag {
fn from(value: LdapFilter) -> Tag {
match value {
LdapFilter::And(vf) => Tag::Set(Set {
id: 0,
class: TagClass::Context,
inner: vf.into_iter().map(|v| v.into()).collect(),
}),
LdapFilter::Or(vf) => Tag::Set(Set {
id: 1,
class: TagClass::Context,
inner: vf.into_iter().map(|v| v.into()).collect(),
}),
LdapFilter::Not(f) => Tag::ExplicitTag(ExplicitTag {
id: 2,
class: TagClass::Context,
inner: Box::new((*f).into()),
}),
LdapFilter::Equality(a, v) => Tag::Sequence(Sequence {
id: 3,
class: TagClass::Context,
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(a),
..Default::default()
}),
Tag::OctetString(OctetString {
inner: Vec::from(v),
..Default::default()
}),
],
}),
LdapFilter::Substring(t, f) => Tag::Sequence(Sequence {
id: 4,
class: TagClass::Context,
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(t),
..Default::default()
}),
Tag::Sequence(Sequence {
inner: {
let mut res = vec![];
if let Some(inner) = f.initial {
res.push(Tag::OctetString(OctetString {
id: 0,
inner: inner.as_bytes().to_vec(),
class: TagClass::Context,
}))
}
f.any.iter().for_each(|v| {
res.push(Tag::OctetString(OctetString {
id: 1,
inner: v.as_bytes().to_vec(),
class: TagClass::Context,
}))
});
if let Some(inner) = f.final_ {
res.push(Tag::OctetString(OctetString {
id: 2,
inner: inner.as_bytes().to_vec(),
class: TagClass::Context,
}))
}
res
},
..Default::default()
}),
],
}),
LdapFilter::GreaterOrEqual(a, v) => Tag::Sequence(Sequence {
id: 5,
class: TagClass::Context,
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(a),
..Default::default()
}),
Tag::OctetString(OctetString {
inner: Vec::from(v),
..Default::default()
}),
],
}),
LdapFilter::LessOrEqual(a, v) => Tag::Sequence(Sequence {
id: 6,
class: TagClass::Context,
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(a),
..Default::default()
}),
Tag::OctetString(OctetString {
inner: Vec::from(v),
..Default::default()
}),
],
}),
LdapFilter::Present(a) => Tag::OctetString(OctetString {
id: 7,
class: TagClass::Context,
inner: Vec::from(a),
}),
LdapFilter::Approx(a, v) => Tag::Sequence(Sequence {
id: 8,
class: TagClass::Context,
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(a),
..Default::default()
}),
Tag::OctetString(OctetString {
inner: Vec::from(v),
..Default::default()
}),
],
}),
LdapFilter::Extensible(f) => Tag::Sequence(Sequence {
id: 9,
class: TagClass::Context,
inner: {
let mut res = vec![];
let LdapMatchingRuleAssertion {
matching_rule,
type_,
match_value,
dn_attributes,
} = f;
if let Some(v) = matching_rule {
res.push(Tag::OctetString(OctetString {
inner: Vec::from(v),
id: 1,
class: TagClass::Context,
}))
}
if let Some(v) = type_ {
res.push(Tag::OctetString(OctetString {
inner: Vec::from(v),
id: 2,
class: TagClass::Context,
}))
}
res.push(Tag::OctetString(OctetString {
inner: Vec::from(match_value),
id: 3,
class: TagClass::Context,
}));
res.push(Tag::Boolean(Boolean {
inner: dn_attributes,
id: 4,
class: TagClass::Context,
}));
res
},
}),
}
}
}
impl TryFrom<Vec<StructureTag>> for LdapSearchRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let base = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or_else(|| {
trace!("invalid basedn");
LdapProtoError::SearchBer
})?;
let scope = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t|
if cfg!(feature = "strict") {
t.match_id(Types::Enumerated as u64)
} else {
Some(t)
}
)
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.ok_or_else(|| {
trace!("invalid scope");
LdapProtoError::SearchBer
})
.and_then(LdapSearchScope::try_from)?;
let aliases = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Enumerated as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.ok_or_else(|| {
trace!("invalid aliases");
LdapProtoError::SearchBer
})
.and_then(LdapDerefAliases::try_from)?;
let sizelimit = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Integer as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.map(|v| v as i32)
.ok_or_else(|| {
trace!("invalid sizelimit");
LdapProtoError::SearchBer
})?;
let timelimit = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Integer as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.map(|v| v as i32)
.ok_or_else(|| {
trace!("invalid timelimit");
LdapProtoError::SearchBer
})?;
let typesonly = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Boolean as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_bool_to_bool)
.ok_or_else(|| {
trace!("invalid typesonly");
LdapProtoError::SearchBer
})?;
let filter = value
.pop()
.and_then(|t| LdapFilter::try_from(t).ok())
.ok_or_else(|| {
trace!("invalid filter");
LdapProtoError::SearchBer
})?;
let attrs = value
.pop()
.map(|attrs| {
trace!(?attrs);
attrs
})
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| {
if cfg!(feature = "strict") {
t.match_id(Types::Sequence as u64)
} else {
Some(t)
}
})
.and_then(|t| {
if cfg!(feature = "strict") {
t.expect_constructed()
} else {
Some(Vec::new())
}
})
.and_then(|vs| {
let r: Option<Vec<_>> = vs
.into_iter()
.map(|bv| {
bv.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
})
.collect();
r
})
.ok_or_else(|| {
trace!("invalid attributes");
LdapProtoError::SearchBer
})?;
Ok(LdapSearchRequest {
base,
scope,
aliases,
sizelimit,
timelimit,
typesonly,
filter,
attrs,
})
}
}
impl From<LdapSearchRequest> for Vec<Tag> {
fn from(value: LdapSearchRequest) -> Vec<Tag> {
let LdapSearchRequest {
base,
scope,
aliases,
sizelimit,
timelimit,
typesonly,
filter,
attrs,
} = value;
vec![
Tag::OctetString(OctetString {
inner: Vec::from(base),
..Default::default()
}),
Tag::Enumerated(Enumerated {
inner: scope as i64,
..Default::default()
}),
Tag::Enumerated(Enumerated {
inner: aliases as i64,
..Default::default()
}),
Tag::Integer(Integer {
inner: sizelimit as i64,
..Default::default()
}),
Tag::Integer(Integer {
inner: timelimit as i64,
..Default::default()
}),
Tag::Boolean(Boolean {
inner: typesonly,
..Default::default()
}),
filter.into(),
Tag::Sequence(Sequence {
inner: attrs
.into_iter()
.map(|v| {
Tag::OctetString(OctetString {
inner: Vec::from(v),
..Default::default()
})
})
.collect(),
..Default::default()
}),
]
}
}
impl TryFrom<StructureTag> for LdapModify {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
let mut inner = value
.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::ModifyBer)?;
inner.reverse();
let operation = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Enumerated as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_integer_to_i64)
.ok_or(LdapProtoError::ModifyBer)
.and_then(LdapModifyType::try_from)?;
let modification = inner
.pop()
.and_then(|t| LdapPartialAttribute::try_from(t).ok())
.ok_or(LdapProtoError::ModifyBer)?;
Ok(Self {
operation,
modification,
})
}
}
impl TryFrom<StructureTag> for LdapPartialAttribute {
type Error = LdapProtoError;
fn try_from(value: StructureTag) -> Result<Self, Self::Error> {
let mut inner = value
.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::PartialAttributeBer)?;
inner.reverse();
let atype = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::PartialAttributeBer)?;
let vals = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Set as u64))
.and_then(|t| t.expect_constructed())
.and_then(|bset| {
let r: Option<Vec<_>> = bset
.into_iter()
.map(|bv| {
bv.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
})
.collect();
r
})
.ok_or(LdapProtoError::PartialAttributeBer)?;
Ok(LdapPartialAttribute { atype, vals })
}
}
impl TryFrom<Vec<StructureTag>> for LdapSearchResultEntry {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::SearchResultEntryBer)?;
let attributes = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.and_then(|bset| {
let r: Result<Vec<_>, _> = bset
.into_iter()
.map(LdapPartialAttribute::try_from)
.collect();
r.ok()
})
.ok_or(LdapProtoError::SearchResultEntryBer)?;
Ok(LdapSearchResultEntry { dn, attributes })
}
}
impl From<LdapPartialAttribute> for Tag {
fn from(value: LdapPartialAttribute) -> Tag {
let LdapPartialAttribute { atype, vals } = value;
Tag::Sequence(Sequence {
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(atype),
..Default::default()
}),
Tag::Set(Set {
inner: vals
.into_iter()
.map(|v| {
Tag::OctetString(OctetString {
inner: v,
..Default::default()
})
})
.collect(),
..Default::default()
}),
],
..Default::default()
})
}
}
impl From<LdapSearchResultEntry> for Vec<Tag> {
fn from(value: LdapSearchResultEntry) -> Vec<Tag> {
let LdapSearchResultEntry { dn, attributes } = value;
vec![
Tag::OctetString(OctetString {
inner: Vec::from(dn),
..Default::default()
}),
Tag::Sequence(Sequence {
inner: attributes.into_iter().map(|v| v.into()).collect(),
..Default::default()
}),
]
}
}
impl TryFrom<Vec<StructureTag>> for LdapExtendedRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let name = value
.pop()
.and_then(|t| t.match_class(TagClass::Context))
.and_then(|t| t.match_id(0))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ExtendedRequestBer)?;
let value = value
.pop()
.and_then(|t| t.match_class(TagClass::Context))
.and_then(|t| t.match_id(1))
.and_then(|t| t.expect_primitive());
Ok(LdapExtendedRequest { name, value })
}
}
impl From<LdapExtendedRequest> for Vec<Tag> {
fn from(value: LdapExtendedRequest) -> Vec<Tag> {
let LdapExtendedRequest { name, value } = value;
once_with(|| {
Tag::OctetString(OctetString {
id: 0,
class: TagClass::Context,
inner: Vec::from(name),
})
})
.chain(
once_with(|| {
value.map(|v| {
Tag::OctetString(OctetString {
id: 1,
class: TagClass::Context,
inner: v,
})
})
})
.flatten(),
)
.collect()
}
}
impl TryFrom<Vec<StructureTag>> for LdapExtendedResponse {
type Error = LdapProtoError;
fn try_from(value: Vec<StructureTag>) -> Result<Self, Self::Error> {
let (res, remtag) = LdapResult::try_from_tag(value)?;
let mut name = None;
let mut value = None;
remtag.into_iter().for_each(|v| {
match (v.id, v.class) {
(10, TagClass::Context) => {
name = v
.expect_primitive()
.and_then(|bv| String::from_utf8(bv).ok())
}
(11, TagClass::Context) => value = v.expect_primitive(),
_ => {
}
}
});
Ok(LdapExtendedResponse { res, name, value })
}
}
impl From<LdapExtendedResponse> for Vec<Tag> {
fn from(value: LdapExtendedResponse) -> Vec<Tag> {
let LdapExtendedResponse { res, name, value } = value;
res.into_tag_iter()
.chain(once_with(|| {
name.map(|v| {
Tag::OctetString(OctetString {
id: 10,
class: TagClass::Context,
inner: Vec::from(v),
})
})
}))
.chain(once_with(|| {
value.map(|v| {
Tag::OctetString(OctetString {
id: 11,
class: TagClass::Context,
inner: v,
})
})
}))
.flatten()
.collect()
}
}
impl LdapExtendedResponse {
pub fn new_success(name: Option<&str>, value: Option<&str>) -> Self {
LdapExtendedResponse {
res: LdapResult {
code: LdapResultCode::Success,
matcheddn: "".to_string(),
message: "".to_string(),
referral: Vec::new(),
},
name: name.map(|v| v.to_string()),
value: value.map(Vec::from),
}
}
pub fn new_operationserror(msg: &str) -> Self {
LdapExtendedResponse {
res: LdapResult {
code: LdapResultCode::OperationsError,
matcheddn: "".to_string(),
message: msg.to_string(),
referral: Vec::new(),
},
name: None,
value: None,
}
}
}
impl TryFrom<Vec<StructureTag>> for LdapIntermediateResponse {
type Error = LdapProtoError;
fn try_from(tags: Vec<StructureTag>) -> Result<Self, Self::Error> {
let mut name = None;
let mut value = None;
tags.into_iter().for_each(|v| {
match (v.id, v.class) {
(0, TagClass::Context) => {
name = v
.expect_primitive()
.and_then(|bv| String::from_utf8(bv).ok())
}
(1, TagClass::Context) => value = v.expect_primitive(),
_ => {
}
}
});
match (name.as_deref(), value.as_ref()) {
(Some("1.3.6.1.4.1.4203.1.9.1.4"), Some(buf)) => {
let mut parser = Parser::new();
let (_rem, msg) = parser
.parse(buf)
.map_err(|_| LdapProtoError::IntermediateResponseBer)?;
if msg.class != TagClass::Context {
error!("Invalid tagclass");
return Err(LdapProtoError::IntermediateResponseTag);
};
let id = msg.id;
let mut inner = msg.expect_constructed().ok_or_else(|| {
trace!("invalid or filter");
LdapProtoError::IntermediateResponseBer
})?;
match id {
0 => {
let cookie =
inner
.pop()
.and_then(|t| t.expect_primitive())
.ok_or_else(|| {
trace!("invalid cookie");
LdapProtoError::IntermediateResponseBer
})?;
Ok(LdapIntermediateResponse::SyncInfoNewCookie { cookie })
}
1 => {
let mut done = true;
let mut cookie = None;
for t in inner
.into_iter()
.filter_map(|t| t.match_class(TagClass::Universal))
{
if t.id == Types::Boolean as u64 {
done = t
.expect_primitive()
.and_then(ber_bool_to_bool)
.ok_or(LdapProtoError::IntermediateResponseBer)?;
} else if t.id == Types::OctetString as u64 {
cookie = t.expect_primitive();
} else {
}
}
Ok(LdapIntermediateResponse::SyncInfoRefreshDelete { cookie, done })
}
2 => {
let done = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Boolean as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_bool_to_bool)
.unwrap_or(true);
let cookie = inner.pop().and_then(|t| t.expect_primitive());
Ok(LdapIntermediateResponse::SyncInfoRefreshPresent { cookie, done })
}
3 => {
let syncuuids = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Set as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::IntermediateResponseBer)
.and_then(|bset| {
let r: Result<Vec<_>, _> = bset
.into_iter()
.map(|bv| {
bv.match_class(TagClass::Universal)
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.ok_or(LdapProtoError::IntermediateResponseBer)
.and_then(|v| {
Uuid::from_slice(&v).map_err(|_| {
error!("Invalid syncUUID");
LdapProtoError::IntermediateResponseSyncUuid
})
})
})
.collect();
r
})?;
let refresh_deletes = inner
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Boolean as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_bool_to_bool)
.unwrap_or(false);
let cookie = inner.pop().and_then(|t| t.expect_primitive());
Ok(LdapIntermediateResponse::SyncInfoIdSet {
cookie,
refresh_deletes,
syncuuids,
})
}
_ => {
trace!("invalid value id");
Err(LdapProtoError::IntermediateResponseId)
}
}
}
_ => Ok(LdapIntermediateResponse::Raw { name, value }),
}
}
}
impl From<LdapIntermediateResponse> for Vec<Tag> {
fn from(value: LdapIntermediateResponse) -> Vec<Tag> {
let (name, value) = match value {
LdapIntermediateResponse::SyncInfoNewCookie { cookie } => {
let inner = vec![Tag::OctetString(OctetString {
inner: cookie,
..Default::default()
})];
let inner_tag = Tag::Sequence(Sequence {
class: TagClass::Context,
id: 0,
inner,
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, inner_tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
(
Some("1.3.6.1.4.1.4203.1.9.1.4".to_string()),
Some(bytes.to_vec()),
)
}
LdapIntermediateResponse::SyncInfoRefreshDelete { cookie, done } => {
let inner = once_with(|| {
cookie.map(|c| {
Tag::OctetString(OctetString {
inner: c,
..Default::default()
})
})
})
.chain(once_with(|| {
if !done {
Some(Tag::Boolean(Boolean {
inner: false,
..Default::default()
}))
} else {
None
}
}))
.flatten()
.collect();
let inner_tag = Tag::Sequence(Sequence {
class: TagClass::Context,
id: 1,
inner,
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, inner_tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
(
Some("1.3.6.1.4.1.4203.1.9.1.4".to_string()),
Some(bytes.to_vec()),
)
}
LdapIntermediateResponse::SyncInfoRefreshPresent { cookie, done } => {
let inner = once_with(|| {
cookie.map(|c| {
Tag::OctetString(OctetString {
inner: c,
..Default::default()
})
})
})
.chain(once_with(|| {
if !done {
Some(Tag::Boolean(Boolean {
inner: false,
..Default::default()
}))
} else {
None
}
}))
.flatten()
.collect();
let inner_tag = Tag::Sequence(Sequence {
class: TagClass::Context,
id: 2,
inner,
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, inner_tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
(
Some("1.3.6.1.4.1.4203.1.9.1.4".to_string()),
Some(bytes.to_vec()),
)
}
LdapIntermediateResponse::SyncInfoIdSet {
cookie,
refresh_deletes,
syncuuids,
} => {
let inner = once_with(|| {
cookie.map(|c| {
Tag::OctetString(OctetString {
inner: c,
..Default::default()
})
})
})
.chain(once_with(|| {
if refresh_deletes {
Some(Tag::Boolean(Boolean {
inner: true,
..Default::default()
}))
} else {
None
}
}))
.chain(once_with(|| {
Some(Tag::Set(Set {
inner: syncuuids
.into_iter()
.map(|entry_uuid| {
Tag::OctetString(OctetString {
inner: entry_uuid.as_bytes().to_vec(),
..Default::default()
})
})
.collect(),
..Default::default()
}))
}))
.flatten()
.collect();
let inner_tag = Tag::Sequence(Sequence {
class: TagClass::Context,
id: 3,
inner,
});
let mut bytes = BytesMut::new();
lber_write::encode_into(&mut bytes, inner_tag.into_structure())
.expect("Failed to encode inner structure, this is a bug!");
(
Some("1.3.6.1.4.1.4203.1.9.1.4".to_string()),
Some(bytes.to_vec()),
)
}
LdapIntermediateResponse::Raw { name, value } => (name, value),
};
once_with(|| {
name.map(|v| {
Tag::OctetString(OctetString {
id: 0,
class: TagClass::Context,
inner: Vec::from(v),
})
})
})
.chain(once_with(|| {
value.map(|v| {
Tag::OctetString(OctetString {
id: 1,
class: TagClass::Context,
inner: v,
})
})
}))
.flatten()
.collect()
}
}
impl TryFrom<i64> for LdapModifyType {
type Error = LdapProtoError;
fn try_from(value: i64) -> Result<Self, Self::Error> {
match value {
0 => Ok(LdapModifyType::Add),
1 => Ok(LdapModifyType::Delete),
2 => Ok(LdapModifyType::Replace),
_ => Err(LdapProtoError::ModifyTypeValue),
}
}
}
impl TryFrom<i64> for LdapSearchScope {
type Error = LdapProtoError;
fn try_from(value: i64) -> Result<Self, Self::Error> {
match value {
0 => Ok(LdapSearchScope::Base),
1 => Ok(LdapSearchScope::OneLevel),
2 => Ok(LdapSearchScope::Subtree),
3 => Ok(LdapSearchScope::Children),
_ => Err(LdapProtoError::SearchScopeValue),
}
}
}
impl TryFrom<i64> for LdapDerefAliases {
type Error = LdapProtoError;
fn try_from(value: i64) -> Result<Self, Self::Error> {
match value {
0 => Ok(LdapDerefAliases::Never),
1 => Ok(LdapDerefAliases::InSearching),
2 => Ok(LdapDerefAliases::FindingBaseObj),
3 => Ok(LdapDerefAliases::Always),
_ => Err(LdapProtoError::DerefAliasesValue),
}
}
}
impl TryFrom<Vec<StructureTag>> for LdapModifyRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ModifyRequestBer)?;
let changes = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::ModifyRequestBer)
.and_then(|bset| {
bset.into_iter()
.map(LdapModify::try_from)
.collect::<Result<Vec<_>, _>>()
})?;
Ok(Self { dn, changes })
}
}
impl TryFrom<Vec<StructureTag>> for LdapAddRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::AddRequestBer)?;
let attributes = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::AddRequestBer)
.and_then(|bset| {
bset.into_iter()
.map(LdapAttribute::try_from)
.collect::<Result<Vec<_>, _>>()
})?;
Ok(LdapAddRequest { dn, attributes })
}
}
impl TryFrom<Vec<StructureTag>> for LdapModifyDNRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ModifyDNRequestBer)?;
let newrdn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::ModifyDNRequestBer)?;
let deleteoldrdn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Boolean as u64))
.and_then(|t| t.expect_primitive())
.and_then(ber_bool_to_bool)
.ok_or(LdapProtoError::ModifyDNRequestBer)?;
let new_superior = value
.pop()
.and_then(|t| t.match_class(TagClass::Context))
.and_then(|t| t.match_id(0))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok());
Ok(Self {
dn,
newrdn,
deleteoldrdn,
new_superior,
})
}
}
impl TryFrom<Vec<StructureTag>> for LdapCompareRequest {
type Error = LdapProtoError;
fn try_from(mut value: Vec<StructureTag>) -> Result<Self, Self::Error> {
value.reverse();
let dn = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::CompareRequestBer)?;
let mut ava = value
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::Sequence as u64))
.and_then(|t| t.expect_constructed())
.ok_or(LdapProtoError::CompareRequestBer)?;
let val = ava
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.ok_or(LdapProtoError::CompareRequestBer)?;
let atype = ava
.pop()
.and_then(|t| t.match_class(TagClass::Universal))
.and_then(|t| t.match_id(Types::OctetString as u64))
.and_then(|t| t.expect_primitive())
.and_then(|bv| String::from_utf8(bv).ok())
.ok_or(LdapProtoError::CompareRequestBer)?;
Ok(Self { dn, atype, val })
}
}
impl From<LdapCompareRequest> for Vec<Tag> {
fn from(value: LdapCompareRequest) -> Vec<Tag> {
let LdapCompareRequest { dn, atype, val } = value;
vec![
Tag::OctetString(OctetString {
inner: Vec::from(dn),
..Default::default()
}),
Tag::Sequence(Sequence {
inner: vec![
Tag::OctetString(OctetString {
inner: Vec::from(atype),
..Default::default()
}),
Tag::OctetString(OctetString {
inner: val,
..Default::default()
}),
],
..Default::default()
}),
]
}
}
impl From<LdapModify> for Tag {
fn from(value: LdapModify) -> Tag {
let LdapModify {
operation,
modification,
} = value;
let inner = vec![
Tag::Enumerated(Enumerated {
inner: operation as i64,
..Default::default()
}),
modification.into(),
];
Tag::Sequence(Sequence {
inner,
..Default::default()
})
}
}
impl From<LdapModifyRequest> for Vec<Tag> {
fn from(value: LdapModifyRequest) -> Vec<Tag> {
let LdapModifyRequest { dn, changes } = value;
vec![
Tag::OctetString(OctetString {
inner: Vec::from(dn),
..Default::default()
}),
Tag::Sequence(Sequence {
inner: changes.into_iter().map(|v| v.into()).collect(),
..Default::default()
}),
]
}
}
impl From<LdapAddRequest> for Vec<Tag> {
fn from(value: LdapAddRequest) -> Vec<Tag> {
let LdapAddRequest { dn, attributes } = value;
vec![
Tag::OctetString(OctetString {
inner: Vec::from(dn),
..Default::default()
}),
Tag::Sequence(Sequence {
inner: attributes.into_iter().map(|v| v.into()).collect(),
..Default::default()
}),
]
}
}
impl From<LdapSearchResultReference> for Vec<Tag> {
fn from(value: LdapSearchResultReference) -> Self {
let LdapSearchResultReference { uris } = value;
let uri_tags: Vec<Tag> = uris
.into_iter()
.map(|uri| {
Tag::OctetString(OctetString {
inner: Vec::from(uri),
..Default::default()
})
})
.collect();
uri_tags
}
}
impl From<LdapModifyDNRequest> for Vec<Tag> {
fn from(value: LdapModifyDNRequest) -> Vec<Tag> {
let LdapModifyDNRequest {
dn,
newrdn,
deleteoldrdn,
new_superior,
} = value;
let mut v = Vec::with_capacity(4);
v.push(Tag::OctetString(OctetString {
inner: Vec::from(dn),
..Default::default()
}));
v.push(Tag::OctetString(OctetString {
inner: Vec::from(newrdn),
..Default::default()
}));
v.push(Tag::Boolean(Boolean {
inner: deleteoldrdn,
..Default::default()
}));
if let Some(ns) = new_superior {
v.push(Tag::OctetString(OctetString {
id: 0,
class: TagClass::Context,
inner: Vec::from(ns),
}))
}
v
}
}
impl TryFrom<i64> for LdapResultCode {
type Error = LdapProtoError;
fn try_from(value: i64) -> Result<Self, Self::Error> {
match value {
0 => Ok(LdapResultCode::Success),
1 => Ok(LdapResultCode::OperationsError),
2 => Ok(LdapResultCode::ProtocolError),
3 => Ok(LdapResultCode::TimeLimitExceeded),
4 => Ok(LdapResultCode::SizeLimitExceeded),
5 => Ok(LdapResultCode::CompareFalse),
6 => Ok(LdapResultCode::CompareTrue),
7 => Ok(LdapResultCode::AuthMethodNotSupported),
8 => Ok(LdapResultCode::StrongerAuthRequired),
10 => Ok(LdapResultCode::Referral),
11 => Ok(LdapResultCode::AdminLimitExceeded),
12 => Ok(LdapResultCode::UnavailableCriticalExtension),
13 => Ok(LdapResultCode::ConfidentialityRequired),
14 => Ok(LdapResultCode::SaslBindInProgress),
16 => Ok(LdapResultCode::NoSuchAttribute),
17 => Ok(LdapResultCode::UndefinedAttributeType),
18 => Ok(LdapResultCode::InappropriateMatching),
19 => Ok(LdapResultCode::ConstraintViolation),
20 => Ok(LdapResultCode::AttributeOrValueExists),
21 => Ok(LdapResultCode::InvalidAttributeSyntax),
32 => Ok(LdapResultCode::NoSuchObject),
33 => Ok(LdapResultCode::AliasProblem),
34 => Ok(LdapResultCode::InvalidDNSyntax),
36 => Ok(LdapResultCode::AliasDereferencingProblem),
48 => Ok(LdapResultCode::InappropriateAuthentication),
49 => Ok(LdapResultCode::InvalidCredentials),
50 => Ok(LdapResultCode::InsufficentAccessRights),
51 => Ok(LdapResultCode::Busy),
52 => Ok(LdapResultCode::Unavailable),
53 => Ok(LdapResultCode::UnwillingToPerform),
54 => Ok(LdapResultCode::LoopDetect),
64 => Ok(LdapResultCode::NamingViolation),
65 => Ok(LdapResultCode::ObjectClassViolation),
66 => Ok(LdapResultCode::NotAllowedOnNonLeaf),
67 => Ok(LdapResultCode::NotALlowedOnRDN),
68 => Ok(LdapResultCode::EntryAlreadyExists),
69 => Ok(LdapResultCode::ObjectClassModsProhibited),
71 => Ok(LdapResultCode::AffectsMultipleDSAs),
80 => Ok(LdapResultCode::Other),
4096 => Ok(LdapResultCode::EsyncRefreshRequired),
i => {
error!("Unknown i64 ecode {}", i);
Err(LdapProtoError::ResultCode)
}
}
}
}
pub(crate) fn ber_bool_to_bool<V: AsRef<[u8]>>(bv: V) -> Option<bool> {
bv.as_ref().first().map(|v| !matches!(v, 0))
}
pub(crate) fn ber_integer_to_i64<V: AsRef<[u8]>>(v: V) -> Option<i64> {
let bv = v.as_ref();
let mut raw: [u8; 8] = [0; 8];
let base = if bv.len() > 8 {
return None;
} else {
8 - bv.len()
};
raw[base..(bv.len() + base)].clone_from_slice(bv);
Some(i64::from_be_bytes(raw))
}
#[derive(Debug, PartialEq, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub struct LdapSearchResultReference {
pub uris: Vec<String>,
}
impl TryFrom<Vec<StructureTag>> for LdapSearchResultReference {
type Error = LdapProtoError;
fn try_from(value: Vec<StructureTag>) -> Result<Self, Self::Error> {
let mut uris = Vec::new();
for tag in value {
if let Some(bytes) = tag.expect_primitive() {
let uri = String::from_utf8(bytes).map_err(|_| LdapProtoError::LdapMsgBer)?;
uris.push(uri);
} else {
Err(LdapProtoError::LdapMsgBer)?;
}
}
Ok(LdapSearchResultReference { uris })
}
}