]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Add m_ldap, and convert m_ldapoper and m_ldapauth to use it.
authorAdam <Adam@anope.org>
Thu, 10 Oct 2013 04:35:04 +0000 (00:35 -0400)
committerAttila Molnar <attilamolnar@hush.com>
Thu, 19 Dec 2013 15:40:11 +0000 (16:40 +0100)
docs/conf/modules.conf.example
include/modules/ldap.h [new file with mode: 0644]
src/modules/extra/m_ldap.cpp [new file with mode: 0644]
src/modules/extra/m_ldapauth.cpp [deleted file]
src/modules/extra/m_ldapoper.cpp [deleted file]
src/modules/m_ldapauth.cpp [new file with mode: 0644]
src/modules/m_ldapoper.cpp [new file with mode: 0644]

index 7cce36cbe4edb4eab7b55066d2c6e7a03a7542e6..ffb5d2a02f732d57ee6a1003674bbbf2dc155d6d 100644 (file)
 # If set to "both" then (surprise!) both will be sent.
 #<knock notify="notice">
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# LDAP module: Allows other SQL modules to access a LDAP database
+# through a unified API.
+# This modules is in extras. Re-run configure with: ./configure --enable-extras=m_ldap.cpp
+# and run make install, then uncomment this module to enable it.
+#
+#<module name="m_ldap.so">
+#<database module="ldap" id="ldapdb" server="ldap://localhost" binddn="cn=Manager,dc=inspircd,dc=org" bindauth="mysecretpass" searchscope="subtree">
+# The server parameter indicates the LDAP server to connect to. The   #
+# ldap:// style scheme before the hostname proper is MANDATORY.       #
+#                                                                     #
+# The binddn and bindauth indicate the DN to bind to for searching,   #
+# and the password for the distinguished name. Some LDAP servers will #
+# allow anonymous searching in which case these two values do not     #
+# need defining, otherwise they should be set similar to the examples #
+# above.                                                              #
+#                                                                     #
+# The searchscope value indicates the subtree to search under. On our #
+# test system this is 'subtree'. Your mileage may vary.               #
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # LDAP authentication module: Adds the ability to authenticate users  #
-# via LDAP. This is an extra module which must be enabled explicitly  #
-# by symlinking it from modules/extra, and requires the OpenLDAP libs #
-# This modules is in extras. To enable it, Re-run configure with:     #
-# ./configure --enable-extras=m_ldapauth.cpp                          #
-# and run make install, then uncomment this module.                   #
+# via LDAP.                                                           #
 #                                                                     #
 #<module name="m_ldapauth.so">                                        #
 #                                                                     #
 # Configuration:                                                      #
 #                                                                     #
-# <ldapauth baserdn="ou=People,dc=brainbox,dc=cc"                     #
+# <ldapauth dbid="ldapdb"                                             #
+#           baserdn="ou=People,dc=brainbox,dc=cc"                     #
 #           attribute="uid"                                           #
-#           server="ldap://brainwave.brainbox.cc"                     #
 #           allowpattern="Guest*"                                     #
 #           killreason="Access denied"                                #
-#           searchscope="subtree"                                     #
-#           binddn="cn=Manager,dc=brainbox,dc=cc"                     #
-#           bindauth="mysecretpass"                                   #
 #           verbose="yes"                                             #
 #           host="$uid.$ou.inspircd.org">                             #
 #                                                                     #
 # The attribute value indicates the attribute which is used to locate #
 # a user account by name. On POSIX systems this is usually 'uid'.     #
 #                                                                     #
-# The server parameter indicates the LDAP server to connect to. The   #
-# ldap:// style scheme before the hostname proper is MANDITORY.       #
-#                                                                     #
 # The allowpattern value allows you to specify a wildcard mask which  #
 # will always be allowed to connect regardless of if they have an     #
 # account, for example guest users.                                   #
 # Killreason indicates the QUIT reason to give to users if they fail  #
 # to authenticate.                                                    #
 #                                                                     #
-# The searchscope value indicates the subtree to search under. On our #
-# test system this is 'subtree'. Your mileage may vary.               #
-#                                                                     #
 # Setting the verbose value causes an oper notice to be sent out for  #
 # every failed authentication to the server, with an error string.    #
 #                                                                     #
-# The binddn and bindauth indicate the DN to bind to for searching,   #
-# and the password for the distinguished name. Some LDAP servers will #
-# allow anonymous searching in which case these two values do not     #
-# need defining, otherwise they should be set similar to the examples #
-# above.                                                              #
-#                                                                     #
 # ldapwhitelist indicates that clients connecting from an IP in the   #
 # provided CIDR do not need to authenticate against LDAP. It can be   #
 # repeated to whitelist multiple CIDRs.                               #
 
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # LDAP oper configuration module: Adds the ability to authenticate    #
-# opers via LDAP. This is an extra module which must be enabled       #
-# explicitly by symlinking it from modules/extra, and requires the    #
-# OpenLDAP libs. Re-run configure with: ./configure --enable-extras=m_ldapoper.cpp
-# and run make install, then uncomment this module to enable it.      #
+# opers via LDAP.                                                     #
 #                                                                     #
 #<module name="m_ldapoper.so">
 #                                                                     #
 # Configuration:                                                      #
 #                                                                     #
-# <ldapoper baserdn="ou=People,dc=brainbox,dc=cc"
-#           server="ldap://brainwave.brainbox.cc"
-#           searchscope="subtree"
-#           binddn="cn=Manager,dc=brainbox,dc=cc"
-#           bindauth="mysecretpass"
+# <ldapoper dbid="ldapdb"
+#           baserdn="ou=People,dc=brainbox,dc=cc"
 #           attribute="uid">
 #                                                                     #
 # Available configuration items are identical to the same items in    #
diff --git a/include/modules/ldap.h b/include/modules/ldap.h
new file mode 100644 (file)
index 0000000..75ab160
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+typedef int LDAPQuery;
+
+class LDAPException : public ModuleException
+{
+ public:
+       LDAPException(const std::string& reason) : ModuleException(reason) { }
+
+       virtual ~LDAPException() throw() { }
+};
+
+struct LDAPModification
+{
+       enum LDAPOperation
+       {
+               LDAP_ADD,
+               LDAP_DEL,
+               LDAP_REPLACE
+       };
+
+       LDAPOperation op;
+       std::string name;
+       std::vector<std::string> values;
+};
+
+typedef std::vector<LDAPModification> LDAPMods;
+
+struct LDAPAttributes : public std::map<std::string, std::vector<std::string> >
+{
+       size_t size(const std::string& attr) const
+       {
+               const std::vector<std::string>& array = this->getArray(attr);
+               return array.size();
+       }
+
+       const std::vector<std::string> keys() const
+       {
+               std::vector<std::string> k;
+               for (const_iterator it = this->begin(), it_end = this->end(); it != it_end; ++it)
+                       k.push_back(it->first);
+               return k;
+       }
+
+       const std::string& get(const std::string& attr) const
+       {
+               const std::vector<std::string>& array = this->getArray(attr);
+               if (array.empty())
+                       throw LDAPException("Empty attribute " + attr + " in LDAPResult::get");
+               return array[0];
+       }
+
+       const std::vector<std::string>& getArray(const std::string& attr) const
+       {
+               const_iterator it = this->find(attr);
+               if (it == this->end())
+                       throw LDAPException("Unknown attribute " + attr + " in LDAPResult::getArray");
+               return it->second;
+       }
+};
+
+struct LDAPResult
+{
+       std::vector<LDAPAttributes> messages;
+       std::string error;
+
+       enum QueryType
+       {
+               QUERY_UNKNOWN,
+               QUERY_BIND,
+               QUERY_SEARCH,
+               QUERY_ADD,
+               QUERY_DELETE,
+               QUERY_MODIFY,
+               QUERY_COMPARE
+       };
+
+       QueryType type;
+       LDAPQuery id;
+
+       LDAPResult()
+               : type(QUERY_UNKNOWN), id(-1)
+       {
+       }
+
+       size_t size() const
+       {
+               return this->messages.size();
+       }
+
+       bool empty() const
+       {
+               return this->messages.empty();
+       }
+
+       const LDAPAttributes& get(size_t sz) const
+       {
+               if (sz >= this->messages.size())
+                       throw LDAPException("Index out of range");
+               return this->messages[sz];
+       }
+
+       const std::string& getError() const
+       {
+               return this->error;
+       }
+};
+
+class LDAPInterface
+{
+ public:
+       ModuleRef creator;
+
+       LDAPInterface(Module* m) : creator(m) { }
+       virtual ~LDAPInterface() { }
+
+       virtual void OnResult(const LDAPResult& r) = 0;
+       virtual void OnError(const LDAPResult& err) = 0;
+};
+
+class LDAPProvider : public DataProvider
+{
+ public:
+       LDAPProvider(Module* Creator, const std::string& Name)
+               : DataProvider(Creator, Name) { }
+
+       /** Attempt to bind to the LDAP server as a manager
+        * @param i The LDAPInterface the result is sent to
+        * @return The query ID
+        */
+       virtual LDAPQuery BindAsManager(LDAPInterface *i) = 0;
+
+       /** Bind to LDAP
+        * @param i The LDAPInterface the result is sent to
+        * @param who The binddn
+        * @param pass The password
+        * @return The query ID
+        */
+       virtual LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) = 0;
+
+       /** Search ldap for the specified filter
+        * @param i The LDAPInterface the result is sent to
+        * @param base The base DN to search
+        * @param filter The filter to apply
+        * @return The query ID
+        */
+       virtual LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) = 0;
+
+       /** Add an entry to LDAP
+        * @param i The LDAPInterface the result is sent to
+        * @param dn The dn of the entry to add
+        * @param attributes The attributes
+        * @return The query ID
+        */
+       virtual LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) = 0;
+
+       /** Delete an entry from LDAP
+        * @param i The LDAPInterface the result is sent to
+        * @param dn The dn of the entry to delete
+        * @return The query ID
+        */
+       virtual LDAPQuery Del(LDAPInterface* i, const std::string& dn) = 0;
+
+       /** Modify an existing entry in LDAP
+        * @param i The LDAPInterface the result is sent to
+        * @param base The base DN to modify
+        * @param attributes The attributes to modify
+        * @return The query ID
+        */
+       virtual LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) = 0;
+
+       /** Compare an attribute in LDAP with our value
+        * @param i The LDAPInterface the result is sent to
+        * @param dn DN to use for comparing
+        * @param attr Attr of DN to compare with
+        * @param val value to compare attr of dn
+        * @return the query ID
+        */
+       virtual LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0;
+};
diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp
new file mode 100644 (file)
index 0000000..63c29ed
--- /dev/null
@@ -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 (file)
index d169085..0000000
+++ /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 (file)
index 67ba9a7..0000000
+++ /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 (file)
index 0000000..9356b2d
--- /dev/null
@@ -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 (file)
index 0000000..cb81e7e
--- /dev/null
@@ -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)