2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013-2014 Adam <Adam@anope.org>
5 * Copyright (C) 2003-2014 Anope Team <team@anope.org>
7 * This file is part of InspIRCd. InspIRCd is free software: you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation, version 2.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "modules/ldap.h"
26 # pragma comment(lib, "libldap.lib")
27 # pragma comment(lib, "liblber.lib")
30 /* $LinkerFlags: -lldap */
32 class LDAPService : public LDAPProvider, public SocketThread
35 reference<ConfigTag> config;
39 time_t last_timeout_check;
41 LDAPMod** BuildMods(const LDAPMods& attributes)
43 LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
44 memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
45 for (unsigned int x = 0; x < attributes.size(); ++x)
47 const LDAPModification& l = attributes[x];
48 LDAPMod* mod = new LDAPMod;
51 if (l.op == LDAPModification::LDAP_ADD)
52 mod->mod_op = LDAP_MOD_ADD;
53 else if (l.op == LDAPModification::LDAP_DEL)
54 mod->mod_op = LDAP_MOD_DELETE;
55 else if (l.op == LDAPModification::LDAP_REPLACE)
56 mod->mod_op = LDAP_MOD_REPLACE;
60 throw LDAPException("Unknown LDAP operation");
62 mod->mod_type = strdup(l.name.c_str());
63 mod->mod_values = new char*[l.values.size() + 1];
64 memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
65 for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
66 if (!l.values[j].empty())
67 mod->mod_values[c++] = strdup(l.values[j].c_str());
72 void FreeMods(LDAPMod** mods)
74 for (unsigned int i = 0; mods[i] != NULL; ++i)
76 LDAPMod* mod = mods[i];
77 if (mod->mod_type != NULL)
79 if (mod->mod_values != NULL)
81 for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
82 free(mod->mod_values[j]);
83 delete[] mod->mod_values;
91 // Only try one connect a minute. It is an expensive blocking operation
92 if (last_connect > ServerInstance->Time() - 60)
93 throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
94 last_connect = ServerInstance->Time();
96 ldap_unbind_ext(this->con, NULL, NULL);
100 void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
105 this->queries[msgid] = std::make_pair(ServerInstance->Time(), i);
106 this->UnlockQueueWakeup();
112 if (last_timeout_check == ServerInstance->Time())
114 last_timeout_check = ServerInstance->Time();
116 for (query_queue::iterator it = this->queries.begin(); it != this->queries.end(); )
118 LDAPQuery msgid = it->first;
119 time_t created = it->second.first;
120 LDAPInterface* i = it->second.second;
123 if (ServerInstance->Time() > created + timeout)
125 LDAPResult* ldap_result = new LDAPResult();
126 ldap_result->id = msgid;
127 ldap_result->error = "Query timed out";
129 this->queries.erase(msgid);
130 this->results.push_back(std::make_pair(i, ldap_result));
132 this->NotifyParent();
138 typedef std::map<LDAPQuery, std::pair<time_t, LDAPInterface*> > query_queue;
139 typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
141 result_queue results;
143 LDAPService(Module* c, ConfigTag* tag)
144 : LDAPProvider(c, "LDAP/" + tag->getString("id"))
145 , con(NULL), config(tag), last_connect(0), last_timeout_check(0)
147 std::string scope = config->getString("searchscope");
149 searchscope = LDAP_SCOPE_BASE;
150 else if (scope == "onelevel")
151 searchscope = LDAP_SCOPE_ONELEVEL;
153 searchscope = LDAP_SCOPE_SUBTREE;
154 timeout = config->getInt("timeout", 5);
163 for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i)
165 LDAPQuery msgid = i->first;
166 LDAPInterface* inter = i->second.second;
168 ldap_abandon_ext(this->con, msgid, NULL, NULL);
173 r.error = "LDAP Interface is going away";
177 this->queries.clear();
179 for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
181 LDAPInterface* inter = i->first;
182 LDAPResult* r = i->second;
184 r->error = "LDAP Interface is going away";
188 this->results.clear();
192 ldap_unbind_ext(this->con, NULL, NULL);
197 std::string server = config->getString("server");
198 int i = ldap_initialize(&this->con, server.c_str());
199 if (i != LDAP_SUCCESS)
200 throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
202 const int version = LDAP_VERSION3;
203 i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
204 if (i != LDAP_OPT_SUCCESS)
206 ldap_unbind_ext(this->con, NULL, NULL);
208 throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
211 const struct timeval tv = { 0, 0 };
212 i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
213 if (i != LDAP_OPT_SUCCESS)
215 ldap_unbind_ext(this->con, NULL, NULL);
217 throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
221 LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
223 std::string binddn = config->getString("binddn");
224 std::string bindauth = config->getString("bindauth");
225 return this->Bind(i, binddn, bindauth);
228 LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
231 cred.bv_val = strdup(pass.c_str());
232 cred.bv_len = pass.length();
235 int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
237 if (ret != LDAP_SUCCESS)
239 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
242 return this->Bind(i, who, pass);
245 throw LDAPException(ldap_err2string(ret));
248 SaveInterface(i, msgid);
252 LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
255 throw LDAPException("No interface");
258 int ret = ldap_search_ext(this->con, base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
259 if (ret != LDAP_SUCCESS)
261 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
264 return this->Search(i, base, filter);
267 throw LDAPException(ldap_err2string(ret));
270 SaveInterface(i, msgid);
274 LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
276 LDAPMod** mods = this->BuildMods(attributes);
278 int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
279 this->FreeMods(mods);
281 if (ret != LDAP_SUCCESS)
283 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
286 return this->Add(i, dn, attributes);
289 throw LDAPException(ldap_err2string(ret));
292 SaveInterface(i, msgid);
296 LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
299 int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
301 if (ret != LDAP_SUCCESS)
303 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
306 return this->Del(i, dn);
309 throw LDAPException(ldap_err2string(ret));
312 SaveInterface(i, msgid);
316 LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
318 LDAPMod** mods = this->BuildMods(attributes);
320 int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
321 this->FreeMods(mods);
323 if (ret != LDAP_SUCCESS)
325 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
328 return this->Modify(i, base, attributes);
331 throw LDAPException(ldap_err2string(ret));
334 SaveInterface(i, msgid);
338 LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
341 cred.bv_val = strdup(val.c_str());
342 cred.bv_len = val.length();
345 int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
348 if (ret != LDAP_SUCCESS)
350 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
353 return this->Compare(i, dn, attr, val);
356 throw LDAPException(ldap_err2string(ret));
359 SaveInterface(i, msgid);
363 void Run() CXX11_OVERRIDE
365 while (!this->GetExitFlag())
368 if (this->queries.empty())
370 this->WaitForQueue();
377 struct timeval tv = { 1, 0 };
379 int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
380 if (rtype <= 0 || this->GetExitFlag())
383 int cur_id = ldap_msgid(result);
387 query_queue::iterator it = this->queries.find(cur_id);
388 if (it == this->queries.end())
391 ldap_msgfree(result);
394 LDAPInterface* i = it->second.second;
395 this->queries.erase(it);
399 LDAPResult* ldap_result = new LDAPResult();
400 ldap_result->id = cur_id;
402 for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
404 int cur_type = ldap_msgtype(cur);
406 LDAPAttributes attributes;
409 char* dn = ldap_get_dn(this->con, cur);
412 attributes["dn"].push_back(dn);
420 ldap_result->type = LDAPResult::QUERY_BIND;
422 case LDAP_RES_SEARCH_ENTRY:
423 ldap_result->type = LDAPResult::QUERY_SEARCH;
426 ldap_result->type = LDAPResult::QUERY_ADD;
428 case LDAP_RES_DELETE:
429 ldap_result->type = LDAPResult::QUERY_DELETE;
431 case LDAP_RES_MODIFY:
432 ldap_result->type = LDAPResult::QUERY_MODIFY;
434 case LDAP_RES_SEARCH_RESULT:
435 // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
436 // then the result set is empty
437 ldap_result->type = LDAPResult::QUERY_SEARCH;
439 case LDAP_RES_COMPARE:
440 ldap_result->type = LDAPResult::QUERY_COMPARE;
448 case LDAP_RES_SEARCH_ENTRY:
450 BerElement* ber = NULL;
451 for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
453 berval** vals = ldap_get_values_len(this->con, cur, attr);
454 int count = ldap_count_values_len(vals);
456 std::vector<std::string> attrs;
457 for (int j = 0; j < count; ++j)
458 attrs.push_back(vals[j]->bv_val);
459 attributes[attr] = attrs;
461 ldap_value_free_len(vals);
471 case LDAP_RES_DELETE:
472 case LDAP_RES_MODIFY:
473 case LDAP_RES_COMPARE:
476 int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
477 if (parse_result != LDAP_SUCCESS)
479 ldap_result->error = ldap_err2string(parse_result);
483 if (cur_type == LDAP_RES_COMPARE)
485 if (errcode != LDAP_COMPARE_TRUE)
486 ldap_result->error = ldap_err2string(errcode);
488 else if (errcode != LDAP_SUCCESS)
489 ldap_result->error = ldap_err2string(errcode);
497 ldap_result->messages.push_back(attributes);
500 ldap_msgfree(result);
503 this->results.push_back(std::make_pair(i, ldap_result));
504 this->UnlockQueueWakeup();
506 this->NotifyParent();
510 void OnNotify() CXX11_OVERRIDE
512 LDAPService::result_queue r;
515 this->results.swap(r);
518 for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
520 LDAPInterface* li = i->first;
521 LDAPResult* res = i->second;
523 if (!res->error.empty())
533 class ModuleLDAP : public Module
535 typedef std::map<std::string, LDAPService*> ServiceMap;
536 ServiceMap LDAPServices;
539 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
543 ConfigTagList tags = ServerInstance->Config->ConfTags("database");
544 for (ConfigIter i = tags.first; i != tags.second; i++)
546 const reference<ConfigTag>& tag = i->second;
548 if (tag->getString("module") != "ldap")
551 std::string id = tag->getString("id");
553 ServiceMap::iterator curr = LDAPServices.find(id);
554 if (curr == LDAPServices.end())
556 LDAPService* conn = new LDAPService(this, tag);
559 ServerInstance->Modules->AddService(*conn);
560 ServerInstance->Threads.Start(conn);
565 LDAPServices.erase(curr);
569 for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
571 LDAPService* conn = i->second;
572 ServerInstance->Modules->DelService(*conn);
578 LDAPServices.swap(conns);
581 void OnUnloadModule(Module* m) CXX11_OVERRIDE
583 for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
585 LDAPService* s = it->second;
587 for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
589 int msgid = it2->first;
590 LDAPInterface* i = it2->second.second;
594 s->queries.erase(msgid);
596 for (unsigned int i = s->results.size(); i > 0; --i)
598 LDAPInterface* li = s->results[i - 1].first;
599 LDAPResult* r = s->results[i - 1].second;
601 if (li->creator == m)
603 s->results.erase(s->results.begin() + i - 1);
613 for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
615 LDAPService* conn = i->second;
622 Version GetVersion() CXX11_OVERRIDE
624 return Version("LDAP support", VF_VENDOR);
628 MODULE_INIT(ModuleLDAP)