/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2020 Christos Triantafyllidis * Copyright (C) 2018-2020 Sadie Powell * Copyright (C) 2014, 2018 Attila Molnar * Copyright (C) 2013-2014 Adam * * 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 . */ #include "inspircd.h" #include "modules/ldap.h" namespace { Module* me; } class LDAPOperBase : public LDAPInterface { protected: const std::string uid; const std::string opername; const std::string password; void Fallback(User* user) { if (!user) return; Command* oper_command = ServerInstance->Parser.GetHandler("OPER"); if (!oper_command) return; CommandBase::Params params; params.push_back(opername); params.push_back(password); ClientProtocol::TagMap tags; oper_command->Handle(user, CommandBase::Params(params, tags)); } void Fallback() { User* user = ServerInstance->FindUUID(uid); Fallback(user); } public: LDAPOperBase(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass) : LDAPInterface(mod) , uid(uuid), opername(oper), password(pass) { } void OnError(const LDAPResult& err) CXX11_OVERRIDE { ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: %s", err.getError().c_str()); Fallback(); delete this; } }; class BindInterface : public LDAPOperBase { public: BindInterface(Module* mod, const std::string& uuid, const std::string& oper, const std::string& pass) : LDAPOperBase(mod, uuid, oper, pass) { } void OnResult(const LDAPResult& r) CXX11_OVERRIDE { User* user = ServerInstance->FindUUID(uid); ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername); if (!user || iter == ServerInstance->Config->oper_blocks.end()) { Fallback(); delete this; return; } OperInfo* ifo = iter->second; user->Oper(ifo); delete this; } }; class SearchInterface : public LDAPOperBase { const std::string provider; bool HandleResult(const LDAPResult& result) { dynamic_reference LDAP(me, provider); if (!LDAP || result.empty()) return false; try { const LDAPAttributes& attr = result.get(0); std::string bindDn = attr.get("dn"); if (bindDn.empty()) return false; LDAP->Bind(new BindInterface(this->creator, uid, opername, password), bindDn, password); } catch (LDAPException& ex) { ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason()); } return true; } public: SearchInterface(Module* mod, const std::string& prov, const std::string &uuid, const std::string& oper, const std::string& pass) : LDAPOperBase(mod, uuid, oper, pass) , provider(prov) { } void OnResult(const LDAPResult& result) CXX11_OVERRIDE { if (!HandleResult(result)) Fallback(); delete this; } }; class AdminBindInterface : public LDAPInterface { const std::string provider; const std::string user; const std::string opername; const std::string password; const std::string base; const std::string what; public: AdminBindInterface(Module* c, const std::string& p, const std::string& u, const std::string& o, const std::string& pa, const std::string& b, const std::string& w) : LDAPInterface(c) , provider(p) , user(u) , opername(o) , password(pa) , base(b) , what(w) { } void OnResult(const LDAPResult& r) CXX11_OVERRIDE { dynamic_reference LDAP(me, provider); if (LDAP) { try { LDAP->Search(new SearchInterface(this->creator, provider, user, opername, password), base, what); } catch (LDAPException& ex) { ServerInstance->SNO->WriteToSnoMask('a', "Error searching LDAP server: " + ex.GetReason()); } } delete this; } void OnError(const LDAPResult& err) CXX11_OVERRIDE { ServerInstance->SNO->WriteToSnoMask('a', "Error binding as manager to LDAP server: " + err.getError()); delete this; } }; class ModuleLDAPOper : public Module { dynamic_reference LDAP; std::string base; std::string attribute; public: ModuleLDAPOper() : LDAP(this, "LDAP") { me = this; } void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper"); LDAP.SetProvider("LDAP/" + tag->getString("dbid")); base = tag->getString("baserdn"); attribute = tag->getString("attribute"); } ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE { if (validated && command == "OPER" && parameters.size() >= 2) { const std::string& opername = parameters[0]; const std::string& password = parameters[1]; ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername); if (it == ServerInstance->Config->oper_blocks.end()) return MOD_RES_PASSTHRU; ConfigTag* tag = it->second->oper_block; if (!tag) return MOD_RES_PASSTHRU; std::string acceptedhosts = tag->getString("host"); if (!InspIRCd::MatchMask(acceptedhosts, user->MakeHost(), user->MakeHostIP())) return MOD_RES_PASSTHRU; if (!LDAP) return MOD_RES_PASSTHRU; try { std::string what = attribute + "=" + opername; LDAP->BindAsManager(new AdminBindInterface(this, LDAP.GetProvider(), user->uuid, opername, password, base, what)); return MOD_RES_DENY; } catch (LDAPException& ex) { ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason()); } } return MOD_RES_PASSTHRU; } Version GetVersion() CXX11_OVERRIDE { return Version("Allows server operators to be authenticated against an LDAP database.", VF_VENDOR); } }; MODULE_INIT(ModuleLDAPOper)