From 449cfa65034ba14739a8be41781d6531f1975674 Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Tue, 26 May 2026 09:29:57 +0100 Subject: [PATCH] Add EscapeDN and EscapeSF to the LDAP API. --- include/modules/ldap.h | 10 ++++++ modules/extra/m_ldap.cpp | 74 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/include/modules/ldap.h b/include/modules/ldap.h index 3caf7ac1a..ae6825efa 100644 --- a/include/modules/ldap.h +++ b/include/modules/ldap.h @@ -166,6 +166,16 @@ class LDAPProvider : public Service * @param attributes The attributes to modify */ virtual void Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) = 0; + + /** Escapes a LDAP string for use in a DN. + * @param str The string to escape. + */ + virtual Anope::string EscapeDN(const Anope::string &str) const = 0; + + /** Escapes a LDAP string for use in a search filter. + * @param str The string to escape. + */ + virtual Anope::string EscapeSF(const Anope::string &str) const = 0; }; #endif // ANOPE_LDAP_H diff --git a/modules/extra/m_ldap.cpp b/modules/extra/m_ldap.cpp index fab11bdfd..27079eb88 100644 --- a/modules/extra/m_ldap.cpp +++ b/modules/extra/m_ldap.cpp @@ -388,6 +388,80 @@ class LDAPService : public LDAPProvider, public Thread, public Condition QueueRequest(mod); } + Anope::string EscapeDN(const Anope::string &str) const anope_override + { + if (str.empty()) + return str; + + Anope::string newstr; + newstr.str().reserve(str.length()); + for (size_t idx = 0; idx < str.length(); ++idx) + { + const char chr = str[idx]; + if (chr == '\0') + { + newstr.append("\\00"); + } + else if (chr == '"' || chr == '+' || chr == ',' || chr == ';' || + chr == '<' || chr == '=' || chr == '>' || chr == '\\') + { + newstr.push_back('\\'); + newstr.push_back(chr); + } + else if (idx == 0 && (chr == '#' || chr == ' ')) + { + newstr.push_back('\\'); + newstr.push_back(chr); + } + else if (idx == str.length() - 1 && chr == ' ') + { + newstr.push_back('\\'); + newstr.push_back(chr); + } + else + { + newstr.push_back(chr); + } + } + + return newstr; + } + + Anope::string EscapeSF(const Anope::string &str) const anope_override + { + if (str.empty()) + return str; + + Anope::string newstr; + newstr.str().reserve(str.length()); + for (size_t idx = 0; idx < str.length(); ++idx) + { + const char chr = str[idx]; + switch (chr) + { + case '\0': + newstr.append("\\00"); + break; + case '(': + newstr.append("\\28"); + break; + case ')': + newstr.append("\\29"); + break; + case '*': + newstr.append("\\2A"); + break; + case '\\': + newstr.append("\\5C"); + break; + default: + newstr.push_back(chr); + break; + } + } + return newstr; + } + private: void BuildReply(int res, LDAPRequest *req) {