diff options
| author | 2024-07-17 00:06:50 +0100 | |
|---|---|---|
| committer | 2024-07-17 00:06:50 +0100 | |
| commit | 889d521e05f2e922683d48c74eb00290e7a2f548 (patch) | |
| tree | b4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /src/modules/m_ldapauth.cpp | |
| parent | Use std::endian from C++20 in the sha1 module. (diff) | |
Shuffle the modules about a bit.
Diffstat (limited to 'src/modules/m_ldapauth.cpp')
| -rw-r--r-- | src/modules/m_ldapauth.cpp | 479 |
1 files changed, 0 insertions, 479 deletions
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp deleted file mode 100644 index cc2dcd2e0..000000000 --- a/src/modules/m_ldapauth.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2020 Joel Sing <joel@sing.id.au> - * Copyright (C) 2019-2023 Sadie Powell <sadie@witchery.services> - * Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com> - * Copyright (C) 2014 Thiago Crepaldi <thiago@thiagocrepaldi.com> - * Copyright (C) 2013-2014, 2017 Adam <Adam@anope.org> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * 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 - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "inspircd.h" -#include "extension.h" -#include "modules/ldap.h" - -namespace -{ - Module* me; - std::string killreason; - BoolExtItem* authed; - bool verbose; - std::string vhost; - StringExtItem* vhosts; - std::vector<std::pair<std::string, std::string>> requiredattributes; -} - -class BindInterface final - : public LDAPInterface -{ - const std::string provider; - const std::string uid; - std::string DN; - bool checkingAttributes = false; - bool passed = false; - int attrCount = 0; - - static std::string SafeReplace(const std::string& text, std::map<std::string, std::string>& replacements) - { - std::string result; - result.reserve(text.length()); - - for (unsigned int i = 0; i < text.length(); ++i) - { - char c = text[i]; - if (c == '$') - { - // find the first nonalpha - i++; - unsigned int start = i; - - while (i < text.length() - 1 && isalpha(text[i + 1])) - ++i; - - std::string key(text, start, (i - start) + 1); - result.append(replacements[key]); - } - else - result.push_back(c); - } - - return result; - } - - static void SetVHost(User* user, const std::string& DN) - { - if (!vhost.empty()) - { - irc::commasepstream stream(DN); - - // mashed map of key:value parts of the DN - std::map<std::string, std::string> dnParts; - - std::string dnPart; - while (stream.GetToken(dnPart)) - { - std::string::size_type pos = dnPart.find('='); - if (pos == std::string::npos) // malformed - continue; - - std::string key(dnPart, 0, pos); - std::string value(dnPart, pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself - dnParts[key] = value; - } - - // change host according to config key - vhosts->Set(user, SafeReplace(vhost, dnParts)); - } - } - -public: - BindInterface(Module* c, const std::string& p, const std::string& u, const std::string& dn) - : LDAPInterface(c) - , provider(p) - , uid(u) - , DN(dn) - { - } - - void OnResult(const LDAPResult& r) override - { - auto* user = ServerInstance->Users.FindUUID(uid); - dynamic_reference<LDAPProvider> LDAP(me, provider); - - if (!user || !LDAP) - { - if (!checkingAttributes || !--attrCount) - delete this; - return; - } - - if (!checkingAttributes && requiredattributes.empty()) - { - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Successful connection from {} (dn={})", user->GetRealMask(), DN); - - // We're done, there are no attributes to check - SetVHost(user, DN); - authed->Set(user); - - delete this; - return; - } - - // Already checked attributes? - if (checkingAttributes) - { - if (!passed) - { - // Only one has to pass - passed = true; - - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Successful connection from {} (dn={})", user->GetRealMask(), DN); - - SetVHost(user, DN); - authed->Set(user); - } - - // Delete this if this is the last ref - if (!--attrCount) - delete this; - - return; - } - - // check required attributes - checkingAttributes = true; - - for (const auto& [attr, val] : requiredattributes) - { - // Note that only one of these has to match for it to be success - ServerInstance->Logs.Debug(MODNAME, "LDAP compare: {}={}", attr, val); - try - { - LDAP->Compare(this, DN, attr, val); - ++attrCount; - } - catch (const LDAPException& ex) - { - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Unable to compare attributes {}={}: {}", attr, val, ex.GetReason()); - } - } - - // Nothing done - if (!attrCount) - { - if (verbose) - { - ServerInstance->SNO.WriteToSnoMask('c', "Forbidden connection from {} (dn={}) (unable to validate attributes)", - user->GetRealMask(), DN); - } - ServerInstance->Users.QuitUser(user, killreason); - delete this; - } - } - - void OnError(const LDAPResult& err) override - { - if (checkingAttributes && --attrCount) - return; - - if (passed) - { - delete this; - return; - } - - auto* user = ServerInstance->Users.FindUUID(uid); - if (user) - { - if (verbose) - { - ServerInstance->SNO.WriteToSnoMask('c', "Forbidden connection from {} ({})", - user->GetRealMask(), err.getError()); - } - ServerInstance->Users.QuitUser(user, killreason); - } - - delete this; - } -}; - -class SearchInterface final - : public LDAPInterface -{ - const std::string provider; - const std::string uid; - -public: - SearchInterface(Module* c, const std::string& p, const std::string& u) - : LDAPInterface(c) - , provider(p) - , uid(u) - { - } - - void OnResult(const LDAPResult& r) override - { - LocalUser* user = ServerInstance->Users.FindUUID<LocalUser>(uid); - dynamic_reference<LDAPProvider> LDAP(me, provider); - if (!LDAP || r.empty() || !user) - { - if (user) - ServerInstance->Users.QuitUser(user, killreason); - delete this; - return; - } - - try - { - const LDAPAttributes& a = r.get(0); - std::string bindDn = a.get("dn"); - if (bindDn.empty()) - { - ServerInstance->Users.QuitUser(user, killreason); - delete this; - return; - } - - LDAP->Bind(new BindInterface(this->creator, provider, uid, bindDn), bindDn, user->password); - } - catch (const LDAPException& ex) - { - ServerInstance->SNO.WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason()); - } - delete this; - } - - void OnError(const LDAPResult& err) override - { - ServerInstance->SNO.WriteToSnoMask('a', "Error searching LDAP server: {}", err.getError()); - auto* user = ServerInstance->Users.FindUUID(uid); - if (user) - ServerInstance->Users.QuitUser(user, killreason); - delete this; - } -}; - -class AdminBindInterface final - : public LDAPInterface -{ - const std::string provider; - const std::string uuid; - const std::string base; - const std::string what; - -public: - AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& b, const std::string& w) - : LDAPInterface(c) - , provider(p) - , uuid(u) - , base(b) - , what(w) - { - } - - void OnResult(const LDAPResult& r) override - { - dynamic_reference<LDAPProvider> LDAP(me, provider); - if (LDAP) - { - try - { - LDAP->Search(new SearchInterface(this->creator, provider, uuid), base, what); - } - catch (const LDAPException& ex) - { - ServerInstance->SNO.WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason()); - } - } - delete this; - } - - void OnError(const LDAPResult& err) override - { - ServerInstance->SNO.WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError()); - delete this; - } -}; - -enum class AuthField - : uint8_t -{ - NICKNAME, - USERNAME, - PASSWORD, -}; - -class ModuleLDAPAuth final - : public Module -{ - dynamic_reference<LDAPProvider> LDAP; - BoolExtItem ldapAuthed; - StringExtItem ldapVhost; - std::string base; - std::string attribute; - std::vector<std::string> exemptions; - AuthField field; - -public: - ModuleLDAPAuth() - : Module(VF_VENDOR, "Allows connecting users to be authenticated against an LDAP database.") - , LDAP(this, "LDAP") - , ldapAuthed(this, "ldapauth", ExtensionType::USER) - , ldapVhost(this, "ldapauth-vhost", ExtensionType::USER) - { - me = this; - authed = &ldapAuthed; - vhosts = &ldapVhost; - } - - void ReadConfig(ConfigStatus& status) override - { - const auto& tag = ServerInstance->Config->ConfValue("ldapauth"); - base = tag->getString("baserdn"); - attribute = tag->getString("attribute"); - killreason = tag->getString("killreason"); - vhost = tag->getString("host"); - // Set to true if failed connects should be reported to operators - verbose = tag->getBool("verbose"); - field = tag->getEnum("field", AuthField::NICKNAME, { - { "nickname", AuthField::USERNAME }, - { "password", AuthField::PASSWORD }, - { "username", AuthField::NICKNAME }, - }); - - LDAP.SetProvider("LDAP/" + tag->getString("dbid")); - - requiredattributes.clear(); - for (const auto& [_, rtag] : ServerInstance->Config->ConfTags("ldaprequire")) - { - const std::string attr = rtag->getString("attribute"); - const std::string val = rtag->getString("value"); - - if (!attr.empty() && !val.empty()) - requiredattributes.emplace_back(attr, val); - } - - exemptions.clear(); - for (const auto& [_, etag] : ServerInstance->Config->ConfTags("ldapexemption")) - { - const std::string mask = etag->getString("mask"); - if (!mask.empty()) - exemptions.push_back(mask); - } - - // Begin v3 config compatibility. - for (const auto& [_, wtag] : ServerInstance->Config->ConfTags("ldapwhitelist")) - { - const std::string cidr = wtag->getString("cidr"); - if (!cidr.empty()) - exemptions.push_back("*!*@" + cidr); - } - - irc::spacesepstream nickstream(tag->getString("allowpattern")); - for (std::string nick; nickstream.GetToken(nick); ) - exemptions.push_back(nick + "!*@*"); - // End v3 config compatibility. - } - - void OnUserConnect(LocalUser* user) override - { - std::string* cc = ldapVhost.Get(user); - if (cc) - { - user->ChangeDisplayedHost(*cc); - ldapVhost.Unset(user); - } - } - - ModResult OnUserRegister(LocalUser* user) override - { - for (const auto& exemption : exemptions) - { - if (InspIRCd::MatchCIDR(user->GetRealMask(), exemption) || InspIRCd::MatchCIDR(user->GetMask(), exemption)) - { - ldapAuthed.Set(user, true); - return MOD_RES_PASSTHRU; - } - } - - if (user->password.empty()) - { - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Forbidden connection from {} (no password provided)", user->GetRealMask()); - ServerInstance->Users.QuitUser(user, killreason); - return MOD_RES_DENY; - } - - if (!LDAP) - { - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Forbidden connection from {} (unable to find LDAP provider)", user->GetRealMask()); - ServerInstance->Users.QuitUser(user, killreason); - return MOD_RES_DENY; - } - - std::string what = attribute + "="; - switch (field) - { - case AuthField::NICKNAME: - what += user->nick; - break; - - case AuthField::USERNAME: - what += user->GetRealUser(); - break; - - case AuthField::PASSWORD: - { - auto pos = user->password.find(':'); - if (pos == std::string::npos) - { - if (verbose) - ServerInstance->SNO.WriteToSnoMask('c', "Forbidden connection from {} (no username provided)", user->GetRealMask()); - ServerInstance->Users.QuitUser(user, killreason); - return MOD_RES_DENY; - } - - what += user->password.substr(0, pos); - user->password.erase(0, pos + 1); - user->password.shrink_to_fit(); - break; - } - } - - try - { - LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, base, what)); - } - catch (const LDAPException& ex) - { - ServerInstance->SNO.WriteToSnoMask('a', "LDAP exception: " + ex.GetReason()); - ServerInstance->Users.QuitUser(user, killreason); - } - - return MOD_RES_DENY; - } - - ModResult OnCheckReady(LocalUser* user) override - { - return ldapAuthed.Get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY; - } -}; - -MODULE_INIT(ModuleLDAPAuth) |
