aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/m_ldapauth.cpp
diff options
context:
space:
mode:
authorGravatar Sadie Powell2024-07-17 00:06:50 +0100
committerGravatar Sadie Powell2024-07-17 00:06:50 +0100
commit889d521e05f2e922683d48c74eb00290e7a2f548 (patch)
treeb4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /src/modules/m_ldapauth.cpp
parentUse 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.cpp479
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)