summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2013-10-10 00:35:04 -0400
committerAttila Molnar <attilamolnar@hush.com>2013-12-19 16:40:11 +0100
commitdbbd3339564b774e5f136657dbc4da565149b852 (patch)
treeb68de179672714e04f5f78fca9c050b410c99a03 /src
parent429a4ddf6ac9fd0f16667ff38a40dc437d9af2d2 (diff)
Add m_ldap, and convert m_ldapoper and m_ldapauth to use it.
Diffstat (limited to 'src')
-rw-r--r--src/modules/extra/m_ldap.cpp572
-rw-r--r--src/modules/extra/m_ldapauth.cpp425
-rw-r--r--src/modules/extra/m_ldapoper.cpp232
-rw-r--r--src/modules/m_ldapauth.cpp391
-rw-r--r--src/modules/m_ldapoper.cpp213
5 files changed, 1176 insertions, 657 deletions
diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp
new file mode 100644
index 000000000..63c29ed3a
--- /dev/null
+++ b/src/modules/extra/m_ldap.cpp
@@ -0,0 +1,572 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@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 "modules/ldap.h"
+
+#include <ldap.h>
+
+#ifdef _WIN32
+# pragma comment(lib, "ldap.lib")
+# pragma comment(lib, "lber.lib")
+#endif
+
+/* $LinkerFlags: -lldap */
+
+class LDAPService : public LDAPProvider, public SocketThread
+{
+ LDAP* con;
+ reference<ConfigTag> config;
+ time_t last_connect;
+ int searchscope;
+
+ LDAPMod** BuildMods(const LDAPMods& attributes)
+ {
+ LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
+ memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
+ for (unsigned int x = 0; x < attributes.size(); ++x)
+ {
+ const LDAPModification& l = attributes[x];
+ LDAPMod* mod = new LDAPMod;
+ mods[x] = mod;
+
+ if (l.op == LDAPModification::LDAP_ADD)
+ mod->mod_op = LDAP_MOD_ADD;
+ else if (l.op == LDAPModification::LDAP_DEL)
+ mod->mod_op = LDAP_MOD_DELETE;
+ else if (l.op == LDAPModification::LDAP_REPLACE)
+ mod->mod_op = LDAP_MOD_REPLACE;
+ else if (l.op != 0)
+ {
+ FreeMods(mods);
+ throw LDAPException("Unknown LDAP operation");
+ }
+ mod->mod_type = strdup(l.name.c_str());
+ mod->mod_values = new char*[l.values.size() + 1];
+ memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
+ for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
+ if (!l.values[j].empty())
+ mod->mod_values[c++] = strdup(l.values[j].c_str());
+ }
+ return mods;
+ }
+
+ void FreeMods(LDAPMod** mods)
+ {
+ for (unsigned int i = 0; mods[i] != NULL; ++i)
+ {
+ LDAPMod* mod = mods[i];
+ if (mod->mod_type != NULL)
+ free(mod->mod_type);
+ if (mod->mod_values != NULL)
+ {
+ for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
+ free(mod->mod_values[j]);
+ delete[] mod->mod_values;
+ }
+ }
+ delete[] mods;
+ }
+
+ void Reconnect()
+ {
+ // Only try one connect a minute. It is an expensive blocking operation
+ if (last_connect > ServerInstance->Time() - 60)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
+ last_connect = ServerInstance->Time();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ Connect();
+ }
+
+ void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
+ {
+ if (i != NULL)
+ {
+ this->LockQueue();
+ this->queries[msgid] = i;
+ this->UnlockQueueWakeup();
+ }
+ }
+
+ public:
+ typedef std::map<int, LDAPInterface*> query_queue;
+ typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
+ query_queue queries;
+ result_queue results;
+
+ LDAPService(Module* c, ConfigTag* tag)
+ : LDAPProvider(c, "LDAP/" + tag->getString("id"))
+ , con(NULL), config(tag), last_connect(0)
+ {
+ std::string scope = config->getString("searchscope");
+ if (scope == "base")
+ searchscope = LDAP_SCOPE_BASE;
+ else if (scope == "onelevel")
+ searchscope = LDAP_SCOPE_ONELEVEL;
+ else
+ searchscope = LDAP_SCOPE_SUBTREE;
+
+ Connect();
+ }
+
+ ~LDAPService()
+ {
+ this->LockQueue();
+
+ for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i)
+ ldap_abandon_ext(this->con, i->first, NULL, NULL);
+ this->queries.clear();
+
+ for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
+ {
+ i->second->error = "LDAP Interface is going away";
+ i->first->OnError(*i->second);
+ }
+ this->results.clear();
+
+ this->UnlockQueue();
+
+ ldap_unbind_ext(this->con, NULL, NULL);
+ }
+
+ void Connect()
+ {
+ std::string server = config->getString("server");
+ int i = ldap_initialize(&this->con, server.c_str());
+ if (i != LDAP_SUCCESS)
+ throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
+ const int version = LDAP_VERSION3;
+ i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (i != LDAP_OPT_SUCCESS)
+ {
+ ldap_unbind_ext(this->con, NULL, NULL);
+ this->con = NULL;
+ throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
+ }
+ }
+
+ LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
+ {
+ std::string binddn = config->getString("binddn");
+ std::string bindauth = config->getString("bindauth");
+ return this->Bind(i, binddn, bindauth);
+ }
+
+ LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
+ {
+ berval cred;
+ cred.bv_val = strdup(pass.c_str());
+ cred.bv_len = pass.length();
+
+ LDAPQuery msgid;
+ int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
+ free(cred.bv_val);
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Bind(i, who, pass);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
+ {
+ if (i == NULL)
+ throw LDAPException("No interface");
+
+ LDAPQuery msgid;
+ int ret = ldap_search_ext(this->con, base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Search(i, base, filter);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPMod** mods = this->BuildMods(attributes);
+ LDAPQuery msgid;
+ int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
+ this->FreeMods(mods);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Add(i, dn, attributes);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
+ {
+ LDAPQuery msgid;
+ int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Del(i, dn);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
+ {
+ LDAPMod** mods = this->BuildMods(attributes);
+ LDAPQuery msgid;
+ int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
+ this->FreeMods(mods);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Modify(i, base, attributes);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
+ {
+ berval cred;
+ cred.bv_val = strdup(val.c_str());
+ cred.bv_len = val.length();
+
+ LDAPQuery msgid;
+ int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
+ free(cred.bv_val);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
+ {
+ this->Reconnect();
+ return this->Compare(i, dn, attr, val);
+ }
+ else
+ throw LDAPException(ldap_err2string(ret));
+ }
+
+ SaveInterface(i, msgid);
+ return msgid;
+ }
+
+ void Run() CXX11_OVERRIDE
+ {
+ while (!this->GetExitFlag())
+ {
+ this->LockQueue();
+ if (this->queries.empty())
+ {
+ this->WaitForQueue();
+ this->UnlockQueue();
+ continue;
+ }
+ else
+ this->UnlockQueue();
+
+ struct timeval tv = { 1, 0 };
+ LDAPMessage* result;
+ int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
+ if (rtype <= 0 || this->GetExitFlag())
+ continue;
+
+ int cur_id = ldap_msgid(result);
+
+ this->LockQueue();
+
+ query_queue::iterator it = this->queries.find(cur_id);
+ if (it == this->queries.end())
+ {
+ this->UnlockQueue();
+ ldap_msgfree(result);
+ continue;
+ }
+ LDAPInterface* i = it->second;
+ this->queries.erase(it);
+
+ this->UnlockQueue();
+
+ LDAPResult* ldap_result = new LDAPResult();
+ ldap_result->id = cur_id;
+
+ for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
+ {
+ int cur_type = ldap_msgtype(cur);
+
+ LDAPAttributes attributes;
+
+ {
+ char* dn = ldap_get_dn(this->con, cur);
+ if (dn != NULL)
+ {
+ attributes["dn"].push_back(dn);
+ ldap_memfree(dn);
+ }
+ }
+
+ switch (cur_type)
+ {
+ case LDAP_RES_BIND:
+ ldap_result->type = LDAPResult::QUERY_BIND;
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ ldap_result->type = LDAPResult::QUERY_SEARCH;
+ break;
+ case LDAP_RES_ADD:
+ ldap_result->type = LDAPResult::QUERY_ADD;
+ break;
+ case LDAP_RES_DELETE:
+ ldap_result->type = LDAPResult::QUERY_DELETE;
+ break;
+ case LDAP_RES_MODIFY:
+ ldap_result->type = LDAPResult::QUERY_MODIFY;
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
+ // then the result set is empty
+ ldap_result->type = LDAPResult::QUERY_SEARCH;
+ break;
+ case LDAP_RES_COMPARE:
+ ldap_result->type = LDAPResult::QUERY_COMPARE;
+ break;
+ default:
+ continue;
+ }
+
+ switch (cur_type)
+ {
+ case LDAP_RES_SEARCH_ENTRY:
+ {
+ BerElement* ber = NULL;
+ for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
+ {
+ berval** vals = ldap_get_values_len(this->con, cur, attr);
+ int count = ldap_count_values_len(vals);
+
+ std::vector<std::string> attrs;
+ for (int j = 0; j < count; ++j)
+ attrs.push_back(vals[j]->bv_val);
+ attributes[attr] = attrs;
+
+ ldap_value_free_len(vals);
+ ldap_memfree(attr);
+ }
+ if (ber != NULL)
+ ber_free(ber, 0);
+
+ break;
+ }
+ case LDAP_RES_BIND:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_COMPARE:
+ {
+ int errcode = -1;
+ int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
+ if (parse_result != LDAP_SUCCESS)
+ {
+ ldap_result->error = ldap_err2string(parse_result);
+ }
+ else
+ {
+ if (cur_type == LDAP_RES_COMPARE)
+ {
+ if (errcode != LDAP_COMPARE_TRUE)
+ ldap_result->error = ldap_err2string(errcode);
+ }
+ else if (errcode != LDAP_SUCCESS)
+ ldap_result->error = ldap_err2string(errcode);
+ }
+ break;
+ }
+ default:
+ continue;
+ }
+
+ ldap_result->messages.push_back(attributes);
+ }
+
+ ldap_msgfree(result);
+
+ this->LockQueue();
+ this->results.push_back(std::make_pair(i, ldap_result));
+ this->UnlockQueueWakeup();
+
+ this->NotifyParent();
+ }
+ }
+
+ void OnNotify() CXX11_OVERRIDE
+ {
+ LDAPService::result_queue r;
+
+ this->LockQueue();
+ this->results.swap(r);
+ this->UnlockQueue();
+
+ for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
+ {
+ LDAPInterface* li = i->first;
+ LDAPResult* res = i->second;
+
+ if (!res->error.empty())
+ li->OnError(*res);
+ else
+ li->OnResult(*res);
+
+ delete res;
+ }
+ }
+};
+
+class ModuleLDAP : public Module
+{
+ typedef std::map<std::string, LDAPService*> ServiceMap;
+ ServiceMap LDAPServices;
+
+ public:
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ServiceMap conns;
+
+ ConfigTagList tags = ServerInstance->Config->ConfTags("database");
+ for (ConfigIter i = tags.first; i != tags.second; i++)
+ {
+ const reference<ConfigTag>& tag = i->second;
+
+ if (tag->getString("module") != "ldap")
+ continue;
+
+ std::string id = tag->getString("id");
+
+ ServiceMap::iterator curr = LDAPServices.find(id);
+ if (curr == LDAPServices.end())
+ {
+ LDAPService* conn = new LDAPService(this, tag);
+ conns[id] = conn;
+
+ ServerInstance->Modules->AddService(*conn);
+ ServerInstance->Threads->Start(conn);
+ }
+ else
+ {
+ conns.insert(*curr);
+ LDAPServices.erase(curr);
+ }
+ }
+
+ for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ ServerInstance->Modules->DelService(*conn);
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+
+ LDAPServices.swap(conns);
+ }
+
+ void OnUnloadModule(Module* m) CXX11_OVERRIDE
+ {
+ for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
+ {
+ LDAPService* s = it->second;
+ s->LockQueue();
+ for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
+ {
+ int msgid = it2->first;
+ LDAPInterface* i = it2->second;
+ ++it2;
+
+ if (i->creator == m)
+ s->queries.erase(msgid);
+ }
+ for (unsigned int i = s->results.size(); i > 0; --i)
+ {
+ LDAPInterface* li = s->results[i - 1].first;
+ LDAPResult* r = s->results[i - 1].second;
+
+ if (li->creator == m)
+ {
+ s->results.erase(s->results.begin() + i - 1);
+ delete r;
+ }
+ }
+ s->UnlockQueue();
+ }
+ }
+
+ ~ModuleLDAP()
+ {
+ for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
+ {
+ LDAPService* conn = i->second;
+ conn->join();
+ conn->OnNotify();
+ delete conn;
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("LDAP support", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAP)
diff --git a/src/modules/extra/m_ldapauth.cpp b/src/modules/extra/m_ldapauth.cpp
deleted file mode 100644
index d1690850d..000000000
--- a/src/modules/extra/m_ldapauth.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
- * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * 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 "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "ldap.lib")
-# pragma comment(lib, "lber.lib")
-#endif
-
-/* $LinkerFlags: -lldap */
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-struct RAIILDAPMessage
-{
- RAIILDAPMessage()
- {
- }
-
- ~RAIILDAPMessage()
- {
- dealloc();
- }
-
- void dealloc()
- {
- ldap_msgfree(msg);
- }
-
- operator LDAPMessage*()
- {
- return msg;
- }
-
- LDAPMessage **operator &()
- {
- return &msg;
- }
-
- LDAPMessage *msg;
-};
-
-class ModuleLDAPAuth : public Module
-{
- LocalIntExt ldapAuthed;
- LocalStringExt ldapVhost;
- std::string base;
- std::string attribute;
- std::string ldapserver;
- std::string allowpattern;
- std::string killreason;
- std::string username;
- std::string password;
- std::string vhost;
- std::vector<std::string> whitelistedcidrs;
- std::vector<std::pair<std::string, std::string> > requiredattributes;
- int searchscope;
- bool verbose;
- bool useusername;
- LDAP *conn;
-
-public:
- ModuleLDAPAuth()
- : ldapAuthed("ldapauth", this)
- , ldapVhost("ldapauth_vhost", this)
- {
- conn = NULL;
- }
-
- ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
- whitelistedcidrs.clear();
- requiredattributes.clear();
-
- base = tag->getString("baserdn");
- attribute = tag->getString("attribute");
- ldapserver = tag->getString("server");
- allowpattern = tag->getString("allowpattern");
- killreason = tag->getString("killreason");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- vhost = tag->getString("host");
- verbose = tag->getBool("verbose"); /* Set to true if failed connects should be reported to operators */
- useusername = tag->getBool("userfield");
-
- ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
-
- for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
- {
- std::string cidr = i->second->getString("cidr");
- if (!cidr.empty()) {
- whitelistedcidrs.push_back(cidr);
- }
- }
-
- ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
-
- for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
- {
- const std::string attr = i->second->getString("attribute");
- const std::string val = i->second->getString("value");
-
- if (!attr.empty() && !val.empty())
- requiredattributes.push_back(make_pair(attr, val));
- }
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP connection failed: %s", ldap_err2string(res));
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "LDAP set protocol to v3 failed: %s", ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- 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.substr(start, (i - start) + 1);
- result.append(replacements[key]);
- } else {
- result.push_back(c);
- }
- }
-
- return result;
- }
-
- void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
- {
- std::string* cc = ldapVhost.get(user);
- if (cc)
- {
- user->ChangeDisplayedHost(cc->c_str());
- ldapVhost.unset(user);
- }
- }
-
- ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
- {
- if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
-
- for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
- {
- if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
- {
- ldapAuthed.set(user,1);
- return MOD_RES_PASSTHRU;
- }
- }
-
- if (!CheckCredentials(user))
- {
- ServerInstance->Users->QuitUser(user, killreason);
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool CheckCredentials(LocalUser* user)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- if (user->password.empty())
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
- return false;
- }
-
- int res;
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = const_cast<char*>(password.c_str());
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP bind failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
-
- RAIILDAPMessage msg;
- std::string what = (attribute + "=" + (useusername ? user->ident : user->nick));
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- // Do a second search, based on password, if it contains a :
- // That is, PASS <user>:<password> will work.
- size_t pos = user->password.find(":");
- if (pos != std::string::npos)
- {
- // manpage says we must deallocate regardless of success or failure
- // since we're about to do another query (and reset msg), first
- // free the old one.
- msg.dealloc();
-
- std::string cutpassword = user->password.substr(0, pos);
- res = ldap_search_ext_s(conn, base.c_str(), searchscope, cutpassword.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg);
-
- if (res == LDAP_SUCCESS)
- {
- // Trim the user: prefix, leaving just 'pass' for later password check
- user->password = user->password.substr(pos + 1);
- }
- }
-
- // It may have found based on user:pass check above.
- if (res != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search failed: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned more than one result: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- LDAPMessage *entry;
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (LDAP search returned no results: %s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
- cred.bv_val = (char*)user->password.data();
- cred.bv_len = user->password.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), ldap_err2string(res));
- return false;
- }
-
- if (!requiredattributes.empty())
- {
- bool authed = false;
-
- for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
- {
- const std::string &attr = it->first;
- const std::string &val = it->second;
-
- struct berval attr_value;
- attr_value.bv_val = const_cast<char*>(val.c_str());
- attr_value.bv_len = val.length();
-
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
-
- authed = (ldap_compare_ext_s(conn, DN, attr.c_str(), &attr_value, NULL, NULL) == LDAP_COMPARE_TRUE);
-
- if (authed)
- break;
- }
-
- if (!authed)
- {
- if (verbose)
- ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Lacks required LDAP attributes)", user->GetFullRealHost().c_str());
- return false;
- }
- }
-
- 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.substr(0, pos);
- std::string value = dnPart.substr(pos + 1, dnPart.length() - pos + 1); // +1s to skip the = itself
- dnParts[key] = value;
- }
-
- // change host according to config key
- ldapVhost.set(user, SafeReplace(vhost, dnParts));
- }
-
- ldapAuthed.set(user,1);
- return true;
- }
-
- ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
- {
- return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
- }
-
- Version GetVersion() CXX11_OVERRIDE
- {
- return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
- }
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/extra/m_ldapoper.cpp b/src/modules/extra/m_ldapoper.cpp
deleted file mode 100644
index 67ba9a7e5..000000000
--- a/src/modules/extra/m_ldapoper.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
- *
- * 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 "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include <ldap.h>
-
-#ifdef _WIN32
-# pragma comment(lib, "ldap.lib")
-# pragma comment(lib, "lber.lib")
-#endif
-
-/* $LinkerFlags: -lldap */
-
-struct RAIILDAPString
-{
- char *str;
-
- RAIILDAPString(char *Str)
- : str(Str)
- {
- }
-
- ~RAIILDAPString()
- {
- ldap_memfree(str);
- }
-
- operator char*()
- {
- return str;
- }
-
- operator std::string()
- {
- return str;
- }
-};
-
-class ModuleLDAPAuth : public Module
-{
- std::string base;
- std::string ldapserver;
- std::string username;
- std::string password;
- std::string attribute;
- int searchscope;
- LDAP *conn;
-
- bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
- {
- OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
- if (it == ServerInstance->Config->oper_blocks.end())
- return false;
-
- ConfigTag* tag = it->second->oper_block;
- if (!tag)
- return false;
-
- std::string acceptedhosts = tag->getString("host");
- std::string hostname = user->ident + "@" + user->host;
- if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
- return false;
-
- if (!LookupOper(opername, inputpass))
- return false;
-
- user->Oper(it->second);
- return true;
- }
-
-public:
- ModuleLDAPAuth()
- : conn(NULL)
- {
- }
-
- ~ModuleLDAPAuth()
- {
- if (conn)
- ldap_unbind_ext(conn, NULL, NULL);
- }
-
- void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
- {
- ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
-
- base = tag->getString("baserdn");
- ldapserver = tag->getString("server");
- std::string scope = tag->getString("searchscope");
- username = tag->getString("binddn");
- password = tag->getString("bindauth");
- attribute = tag->getString("attribute");
-
- if (scope == "base")
- searchscope = LDAP_SCOPE_BASE;
- else if (scope == "onelevel")
- searchscope = LDAP_SCOPE_ONELEVEL;
- else searchscope = LDAP_SCOPE_SUBTREE;
-
- Connect();
- }
-
- bool Connect()
- {
- if (conn != NULL)
- ldap_unbind_ext(conn, NULL, NULL);
- int res, v = LDAP_VERSION3;
- res = ldap_initialize(&conn, ldapserver.c_str());
- if (res != LDAP_SUCCESS)
- {
- conn = NULL;
- return false;
- }
-
- res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
- if (res != LDAP_SUCCESS)
- {
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- return true;
- }
-
- ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line) CXX11_OVERRIDE
- {
- if (validated && command == "OPER" && parameters.size() >= 2)
- {
- if (HandleOper(user, parameters[0], parameters[1]))
- return MOD_RES_DENY;
- }
- return MOD_RES_PASSTHRU;
- }
-
- bool LookupOper(const std::string& opername, const std::string& opassword)
- {
- if (conn == NULL)
- if (!Connect())
- return false;
-
- int res;
- char* authpass = strdup(password.c_str());
- // bind anonymously if no bind DN and authentication are given in the config
- struct berval cred;
- cred.bv_val = authpass;
- cred.bv_len = password.length();
-
- if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
- {
- if (res == LDAP_SERVER_DOWN)
- {
- // Attempt to reconnect if the connection dropped
- ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
- Connect();
- res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
- }
-
- if (res != LDAP_SUCCESS)
- {
- free(authpass);
- ldap_unbind_ext(conn, NULL, NULL);
- conn = NULL;
- return false;
- }
- }
- free(authpass);
-
- LDAPMessage *msg, *entry;
- std::string what = attribute + "=" + opername;
- if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
- {
- return false;
- }
- if (ldap_count_entries(conn, msg) > 1)
- {
- ldap_msgfree(msg);
- return false;
- }
- if ((entry = ldap_first_entry(conn, msg)) == NULL)
- {
- ldap_msgfree(msg);
- return false;
- }
- authpass = strdup(opassword.c_str());
- cred.bv_val = authpass;
- cred.bv_len = opassword.length();
- RAIILDAPString DN(ldap_get_dn(conn, entry));
- if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
- {
- free(authpass);
- ldap_msgfree(msg);
- return true;
- }
- else
- {
- free(authpass);
- ldap_msgfree(msg);
- return false;
- }
- }
-
- Version GetVersion() CXX11_OVERRIDE
- {
- return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
- }
-
-};
-
-MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
new file mode 100644
index 000000000..9356b2dd1
--- /dev/null
+++ b/src/modules/m_ldapauth.cpp
@@ -0,0 +1,391 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2011 Pierre Carrier <pierre@spotify.com>
+ * Copyright (C) 2009-2010 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * 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 "modules/ldap.h"
+
+namespace
+{
+ Module* me;
+ std::string killreason;
+ LocalIntExt* authed;
+ bool verbose;
+ std::string vhost;
+ LocalStringExt* vhosts;
+ std::vector<std::pair<std::string, std::string> > requiredattributes;
+}
+
+class BindInterface : public LDAPInterface
+{
+ const std::string provider;
+ const std::string uid;
+ std::string DN;
+ bool checkingAttributes;
+ bool passed;
+ int attrCount;
+
+ 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.substr(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.substr(0, pos);
+ std::string value = dnPart.substr(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), checkingAttributes(false), passed(false), attrCount(0)
+ {
+ }
+
+ void OnResult(const LDAPResult& r) CXX11_OVERRIDE
+ {
+ User* user = ServerInstance->FindUUID(uid);
+ dynamic_reference<LDAPProvider> LDAP(me, provider);
+
+ if (!user || !LDAP)
+ {
+ if (!checkingAttributes || !--attrCount)
+ delete this;
+ return;
+ }
+
+ if (!checkingAttributes && requiredattributes.empty())
+ {
+ // We're done, there are no attributes to check
+ SetVHost(user, DN);
+ authed->set(user, 1);
+
+ delete this;
+ return;
+ }
+
+ // Already checked attributes?
+ if (checkingAttributes)
+ {
+ if (!passed)
+ {
+ // Only one has to pass
+ passed = true;
+
+ SetVHost(user, DN);
+ authed->set(user, 1);
+ }
+
+ // Delete this if this is the last ref
+ if (!--attrCount)
+ delete this;
+
+ return;
+ }
+
+ // check required attributes
+ checkingAttributes = true;
+
+ for (std::vector<std::pair<std::string, std::string> >::const_iterator it = requiredattributes.begin(); it != requiredattributes.end(); ++it)
+ {
+ // Note that only one of these has to match for it to be success
+ const std::string& attr = it->first;
+ const std::string& val = it->second;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "LDAP compare: %s=%s", attr.c_str(), val.c_str());
+ try
+ {
+ LDAP->Compare(this, DN, attr, val);
+ ++attrCount;
+ }
+ catch (LDAPException &ex)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Unable to compare attributes %s=%s: %s", attr.c_str(), val.c_str(), ex.GetReason().c_str());
+ }
+ }
+
+ // Nothing done
+ if (!attrCount)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (unable to validate attributes)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+ }
+
+ void OnError(const LDAPResult& err) CXX11_OVERRIDE
+ {
+ if (checkingAttributes && --attrCount)
+ return;
+
+ if (passed)
+ {
+ delete this;
+ return;
+ }
+
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (%s)", user->GetFullRealHost().c_str(), err.getError().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ delete this;
+ }
+};
+
+class SearchInterface : 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) CXX11_OVERRIDE
+ {
+ LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(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())
+ {
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ return;
+ }
+
+ LDAP->Bind(new BindInterface(this->creator, provider, uid, bindDn), bindDn, user->password);
+ }
+ 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 searching LDAP server: %s", err.getError().c_str());
+ User* user = ServerInstance->FindUUID(uid);
+ if (user)
+ ServerInstance->Users->QuitUser(user, killreason);
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ LocalIntExt ldapAuthed;
+ LocalStringExt ldapVhost;
+ std::string base;
+ std::string attribute;
+ std::string allowpattern;
+ std::vector<std::string> whitelistedcidrs;
+ bool useusername;
+
+public:
+ ModuleLDAPAuth()
+ : LDAP(this, "LDAP")
+ , ldapAuthed("ldapauth", this)
+ , ldapVhost("ldapauth_vhost", this)
+ {
+ me = this;
+ authed = &ldapAuthed;
+ vhosts = &ldapVhost;
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("ldapauth");
+ whitelistedcidrs.clear();
+ requiredattributes.clear();
+
+ base = tag->getString("baserdn");
+ attribute = tag->getString("attribute");
+ allowpattern = tag->getString("allowpattern");
+ killreason = tag->getString("killreason");
+ vhost = tag->getString("host");
+ // Set to true if failed connects should be reported to operators
+ verbose = tag->getBool("verbose");
+ useusername = tag->getBool("userfield");
+
+ LDAP.SetProvider("LDAP/" + tag->getString("dbid"));
+
+ ConfigTagList whitelisttags = ServerInstance->Config->ConfTags("ldapwhitelist");
+
+ for (ConfigIter i = whitelisttags.first; i != whitelisttags.second; ++i)
+ {
+ std::string cidr = i->second->getString("cidr");
+ if (!cidr.empty()) {
+ whitelistedcidrs.push_back(cidr);
+ }
+ }
+
+ ConfigTagList attributetags = ServerInstance->Config->ConfTags("ldaprequire");
+
+ for (ConfigIter i = attributetags.first; i != attributetags.second; ++i)
+ {
+ const std::string attr = i->second->getString("attribute");
+ const std::string val = i->second->getString("value");
+
+ if (!attr.empty() && !val.empty())
+ requiredattributes.push_back(make_pair(attr, val));
+ }
+ }
+
+ void OnUserConnect(LocalUser *user) CXX11_OVERRIDE
+ {
+ std::string* cc = ldapVhost.get(user);
+ if (cc)
+ {
+ user->ChangeDisplayedHost(cc->c_str());
+ ldapVhost.unset(user);
+ }
+ }
+
+ ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
+ {
+ if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+
+ for (std::vector<std::string>::iterator i = whitelistedcidrs.begin(); i != whitelistedcidrs.end(); i++)
+ {
+ if (InspIRCd::MatchCIDR(user->GetIPString(), *i, ascii_case_insensitive_map))
+ {
+ ldapAuthed.set(user,1);
+ return MOD_RES_PASSTHRU;
+ }
+ }
+
+ if (user->password.empty())
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (No password provided)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ if (!LDAP)
+ {
+ if (verbose)
+ ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s (Unable to find LDAP provider)", user->GetFullRealHost().c_str());
+ ServerInstance->Users->QuitUser(user, killreason);
+ return MOD_RES_DENY;
+ }
+
+ try
+ {
+ LDAP->BindAsManager(NULL);
+
+ std::string what = attribute + "=" + (useusername ? user->ident : user->nick);
+ LDAP->Search(new SearchInterface(this, LDAP.GetProvider(), user->uuid), base, what);
+ }
+ catch (LDAPException &ex)
+ {
+ ServerInstance->SNO->WriteToSnoMask('a', "LDAP exception: " + ex.GetReason());
+ ServerInstance->Users->QuitUser(user, killreason);
+ }
+
+ return MOD_RES_DENY;
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)
diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp
new file mode 100644
index 000000000..cb81e7e18
--- /dev/null
+++ b/src/modules/m_ldapoper.cpp
@@ -0,0 +1,213 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
+ *
+ * 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 "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;
+
+ std::vector<std::string> params;
+ params.push_back(opername);
+ params.push_back(password);
+ oper_command->Handle(params, user);
+ }
+
+ 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);
+ OperIndex::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<LDAPProvider> 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, User* user, const std::string& oper, const std::string& pass)
+ : LDAPOperBase(mod, user->uuid, oper, pass)
+ , provider(prov)
+ {
+ }
+
+ void OnResult(const LDAPResult& result) CXX11_OVERRIDE
+ {
+ if (!HandleResult(result))
+ Fallback();
+ delete this;
+ }
+};
+
+class ModuleLDAPAuth : public Module
+{
+ dynamic_reference<LDAPProvider> LDAP;
+ std::string base;
+ std::string attribute;
+
+ public:
+ ModuleLDAPAuth()
+ : 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, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line) CXX11_OVERRIDE
+ {
+ if (validated && command == "OPER" && parameters.size() >= 2)
+ {
+ const std::string& opername = parameters[0];
+ const std::string& password = parameters[1];
+
+ OperIndex::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");
+ std::string hostname = user->ident + "@" + user->host;
+ if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
+ return MOD_RES_PASSTHRU;
+
+ if (!LDAP)
+ return MOD_RES_PASSTHRU;
+
+ try
+ {
+ // First, bind as the manager so the following search will go through
+ LDAP->BindAsManager(NULL);
+
+ // Fire off the search
+ std::string what = attribute + "=" + opername;
+ LDAP->Search(new SearchInterface(this, LDAP.GetProvider(), user, 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("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleLDAPAuth)