inejge / ldap3

A pure-Rust LDAP library using the Tokio stack
Apache License 2.0
220 stars 38 forks source link

"LdapError - FilterParsing" does not explain what problem actually is #57

Closed annmarie-switzer closed 3 years ago

annmarie-switzer commented 3 years ago

The library will sometimes return a FilterParsing error, but it's not clear why this parsing error occurs.

I am using the library to get a list of DN's for objectClass=Group. I am then iterating over those DN's to get Objects associated with those Groups.

Since I am able to retrieve the Group DN from Active Directory with no error, I assume that the DN is valid. Therefore, it is a mystery as to why that same DN might fail when used in a secondary filter to find it's associated Objects. Can you help me understand why this error is occurring?

I would also note that I do not believe special characters in the filter string to be an issue. I have tried various hand-written methods to escape the appropriate characters, as well as this library's built-in methods, but neither work.

code:

let groups_filter = "(&(objectClass=group))";
let group_dns: Vec<_> = LdapIter::new(base_dn, groups_filter, Scope::Subtree, &mut ldap)
    .unwrap()
    .map(|e| e.dn)
    .collect();

for group_dn in group_dns {
    println!("{}", &group_dn)
    // CN=SQLServer2005MSSQLUser$CLTDC10$MICROSOFT\#\#SSEE,CN=Users,DC=My,DC=domain

    let members_filter: String = format!(
        "(|(&(objectClass=user)(!(objectClass=computer))(memberOf={g}))(&(objectClass=group)(memberOf={g})))",
        g=group_dn
    );

    let search_stream = LdapIter::new(base_dn, &members_filter, Scope::Subtree, &mut ldap).unwrap();
    // thread 'async-std/runtime' panicked at 'called `Result::unwrap()` on an `Err` value: FilterParsing'
}

struct LdapIter<'a, 'b> {
    search_stream: EntryStream<'a, 'b, &'a str>,
    results: Vec<SearchEntry>,
    curr: usize,
}

impl<'a, 'b> LdapIter<'a, 'b> {
    fn new(
        base_dn: &'a str,
        filter: &'a str,
        scope: Scope,
        ldap: &'b mut LdapConn
    ) -> Result<Self, LdapError> {
        let adapters: Vec<Box<dyn Adapter<_>>> = vec![
            Box::new(EntriesOnly::new()),
            Box::new(PagedResults::new(999)),
        ];

        let search_stream = ldap.streaming_search_with(
            adapters,
            base_dn,
            scope,
            filter,
            vec!["*"]
        )?;

        let s = Self {
            search_stream,
            results: Vec::new(),
            curr: 0
        };

        Ok(s)
    }
}

impl<'a, 'b> Iterator for LdapIter<'a, 'b> {
    type Item = SearchEntry;
    fn next(&mut self) -> Option<Self::Item> {
        if self.results.is_empty() {
            let res = self.search_stream.next().unwrap();

            match res {
                Some(res) => {
                    let search_entry = SearchEntry::construct(res);
                    self.results.push(search_entry);
                },
                None => return None,
            }
        }

        if self.results.len() <= self.curr {
            let res = self.search_stream.next().unwrap();

            match res {
                Some(res) => {
                    self.results = Vec::new();
                    self.curr = 0;
                    let search_entry = SearchEntry::construct(res);
                    self.results.push(search_entry);
                }
                None => return None,
            }
        }

        let res = self.results[self.curr].clone();
        self.curr += 1;

        Some(res)
    }
}
inejge commented 3 years ago

RFC 4515 allows only hex-digit escape sequences in the LDAP filter string representation, and your group_dn value contains \#\#, which is invalid. Now, ldap3::ldap_escape() should massage an arbitrary string into a representation acceptable to the filter parser. Replacing

g=group_dn

with

g=ldap3::ldap_escape(group_dn)

produces a filter string that must not return a filter parsing error. Does it actually? If not, does it return the expected result?

annmarie-switzer commented 3 years ago

Yep - it works, just as you say it should. IDK what I did this morning but I clearly had something misaligned. I really appreciate your help and prompt response!

inejge commented 3 years ago

I'll close this since I reckon that the question has been answered.