diff --git a/include/modules/ldap.h b/include/modules/ldap.h index c1a7dc6f9..3910bcc78 100644 --- a/include/modules/ldap.h +++ b/include/modules/ldap.h @@ -173,4 +173,14 @@ public: * @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; }; diff --git a/modules/chanstats.cpp b/modules/chanstats.cpp index 832e15b14..9ee4707c9 100644 --- a/modules/chanstats.cpp +++ b/modules/chanstats.cpp @@ -353,14 +353,16 @@ class MChanstats final "(nick_, '', 'total'), (nick_, '', 'monthly')," "(nick_, '', 'weekly'), (nick_, '', 'daily');" "END IF;" + "SET @echan = chan_;" + "SET @enick = nick_;" "SET @update_query = CONCAT('UPDATE `" + prefix + "chanstats` SET line=line+', line_, '," "letters=letters+', letters_, ' , words=words+', words_, ', actions=actions+', actions_, ', " "smileys_happy=smileys_happy+', sm_h_, ', smileys_sad=smileys_sad+', sm_s_, ', " "smileys_other=smileys_other+', sm_o_, ', kicks=kicks+', kicks_, ', kicked=kicked+', kicked_, ', " "modes=modes+', modes_, ', topics=topics+', topics_, ', ', time_ , '=', time_, '+', line_ ,' " - "WHERE (nick='''' OR nick=''', nick_, ''') AND (chan='''' OR chan=''', chan_, ''')');" + "WHERE (nick='''' OR nick=?) AND (chan='''' OR chan=?)');" "PREPARE update_query FROM @update_query;" - "EXECUTE update_query;" + "EXECUTE update_query using @enick, @echan;" "DEALLOCATE PREPARE update_query;" "END"; this->RunQuery(query); diff --git a/modules/extra/ldap.cpp b/modules/extra/ldap.cpp index 424d19225..f8dffb358 100644 --- a/modules/extra/ldap.cpp +++ b/modules/extra/ldap.cpp @@ -405,6 +405,80 @@ public: QueueRequest(mod); } + Anope::string EscapeDN(const Anope::string &str) const 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 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) { diff --git a/modules/ldap_authentication.cpp b/modules/ldap_authentication.cpp index 757b6c197..8cea66eb4 100644 --- a/modules/ldap_authentication.cpp +++ b/modules/ldap_authentication.cpp @@ -92,8 +92,8 @@ public: if (ii->admin_bind) { auto sf = Anope::Template(search_filter, { - { "account", ii->req->GetAccount() }, - { "object_class", object_class }, + { "account", ii->lprov->EscapeSF(ii->req->GetAccount()) }, + { "object_class", object_class }, }); try { @@ -310,7 +310,7 @@ public: attributes[3].name = this->password_attribute; attributes[3].values.push_back(pass); - Anope::string new_dn = username_attribute + "=" + na->nick + "," + basedn; + Anope::string new_dn = username_attribute + "=" + this->ldap->EscapeDN(na->nick) + "," + basedn; this->ldap->Add(&this->orinterface, new_dn, attributes); } diff --git a/modules/ldap_oper.cpp b/modules/ldap_oper.cpp index 6b9195eae..85ace61b1 100644 --- a/modules/ldap_oper.cpp +++ b/modules/ldap_oper.cpp @@ -125,13 +125,13 @@ public: if (!this->binddn.empty()) { auto bdn = Anope::Template(this->binddn, { - { "account", u->Account()->display }, + { "account", this->ldap->EscapeDN(u->Account()->display) }, }); this->ldap->Bind(NULL, bdn, this->password.c_str()); } auto af = Anope::Template(this->filter, { - { "account", u->Account()->display }, + { "account", this->ldap->EscapeSF(u->Account()->display) }, }); this->ldap->Search(new IdentifyInterface(this, u), this->basedn, af); }