2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013 Adam <Adam@anope.org>
5 * Copyright (C) 2003-2013 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, "ldap.lib")
27 # pragma comment(lib, "lber.lib")
30 /* $LinkerFlags: -lldap */
32 class LDAPService : public LDAPProvider, public SocketThread
35 reference<ConfigTag> config;
39 LDAPMod** BuildMods(const LDAPMods& attributes)
41 LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
42 memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
43 for (unsigned int x = 0; x < attributes.size(); ++x)
45 const LDAPModification& l = attributes[x];
46 LDAPMod* mod = new LDAPMod;
49 if (l.op == LDAPModification::LDAP_ADD)
50 mod->mod_op = LDAP_MOD_ADD;
51 else if (l.op == LDAPModification::LDAP_DEL)
52 mod->mod_op = LDAP_MOD_DELETE;
53 else if (l.op == LDAPModification::LDAP_REPLACE)
54 mod->mod_op = LDAP_MOD_REPLACE;
58 throw LDAPException("Unknown LDAP operation");
60 mod->mod_type = strdup(l.name.c_str());
61 mod->mod_values = new char*[l.values.size() + 1];
62 memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
63 for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
64 if (!l.values[j].empty())
65 mod->mod_values[c++] = strdup(l.values[j].c_str());
70 void FreeMods(LDAPMod** mods)
72 for (unsigned int i = 0; mods[i] != NULL; ++i)
74 LDAPMod* mod = mods[i];
75 if (mod->mod_type != NULL)
77 if (mod->mod_values != NULL)
79 for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
80 free(mod->mod_values[j]);
81 delete[] mod->mod_values;
89 // Only try one connect a minute. It is an expensive blocking operation
90 if (last_connect > ServerInstance->Time() - 60)
91 throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
92 last_connect = ServerInstance->Time();
94 ldap_unbind_ext(this->con, NULL, NULL);
98 void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
103 this->queries[msgid] = i;
104 this->UnlockQueueWakeup();
109 typedef std::map<int, LDAPInterface*> query_queue;
110 typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
112 result_queue results;
114 LDAPService(Module* c, ConfigTag* tag)
115 : LDAPProvider(c, "LDAP/" + tag->getString("id"))
116 , con(NULL), config(tag), last_connect(0)
118 std::string scope = config->getString("searchscope");
120 searchscope = LDAP_SCOPE_BASE;
121 else if (scope == "onelevel")
122 searchscope = LDAP_SCOPE_ONELEVEL;
124 searchscope = LDAP_SCOPE_SUBTREE;
133 for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i)
134 ldap_abandon_ext(this->con, i->first, NULL, NULL);
135 this->queries.clear();
137 for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
139 i->second->error = "LDAP Interface is going away";
140 i->first->OnError(*i->second);
142 this->results.clear();
146 ldap_unbind_ext(this->con, NULL, NULL);
151 std::string server = config->getString("server");
152 int i = ldap_initialize(&this->con, server.c_str());
153 if (i != LDAP_SUCCESS)
154 throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
155 const int version = LDAP_VERSION3;
156 i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
157 if (i != LDAP_OPT_SUCCESS)
159 ldap_unbind_ext(this->con, NULL, NULL);
161 throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
165 LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
167 std::string binddn = config->getString("binddn");
168 std::string bindauth = config->getString("bindauth");
169 return this->Bind(i, binddn, bindauth);
172 LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
175 cred.bv_val = strdup(pass.c_str());
176 cred.bv_len = pass.length();
179 int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
181 if (ret != LDAP_SUCCESS)
183 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
186 return this->Bind(i, who, pass);
189 throw LDAPException(ldap_err2string(ret));
192 SaveInterface(i, msgid);
196 LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
199 throw LDAPException("No interface");
202 int ret = ldap_search_ext(this->con, base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
203 if (ret != LDAP_SUCCESS)
205 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
208 return this->Search(i, base, filter);
211 throw LDAPException(ldap_err2string(ret));
214 SaveInterface(i, msgid);
218 LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
220 LDAPMod** mods = this->BuildMods(attributes);
222 int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
223 this->FreeMods(mods);
225 if (ret != LDAP_SUCCESS)
227 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
230 return this->Add(i, dn, attributes);
233 throw LDAPException(ldap_err2string(ret));
236 SaveInterface(i, msgid);
240 LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
243 int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
245 if (ret != LDAP_SUCCESS)
247 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
250 return this->Del(i, dn);
253 throw LDAPException(ldap_err2string(ret));
256 SaveInterface(i, msgid);
260 LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
262 LDAPMod** mods = this->BuildMods(attributes);
264 int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
265 this->FreeMods(mods);
267 if (ret != LDAP_SUCCESS)
269 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
272 return this->Modify(i, base, attributes);
275 throw LDAPException(ldap_err2string(ret));
278 SaveInterface(i, msgid);
282 LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
285 cred.bv_val = strdup(val.c_str());
286 cred.bv_len = val.length();
289 int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
292 if (ret != LDAP_SUCCESS)
294 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
297 return this->Compare(i, dn, attr, val);
300 throw LDAPException(ldap_err2string(ret));
303 SaveInterface(i, msgid);
307 void Run() CXX11_OVERRIDE
309 while (!this->GetExitFlag())
312 if (this->queries.empty())
314 this->WaitForQueue();
321 struct timeval tv = { 1, 0 };
323 int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
324 if (rtype <= 0 || this->GetExitFlag())
327 int cur_id = ldap_msgid(result);
331 query_queue::iterator it = this->queries.find(cur_id);
332 if (it == this->queries.end())
335 ldap_msgfree(result);
338 LDAPInterface* i = it->second;
339 this->queries.erase(it);
343 LDAPResult* ldap_result = new LDAPResult();
344 ldap_result->id = cur_id;
346 for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
348 int cur_type = ldap_msgtype(cur);
350 LDAPAttributes attributes;
353 char* dn = ldap_get_dn(this->con, cur);
356 attributes["dn"].push_back(dn);
364 ldap_result->type = LDAPResult::QUERY_BIND;
366 case LDAP_RES_SEARCH_ENTRY:
367 ldap_result->type = LDAPResult::QUERY_SEARCH;
370 ldap_result->type = LDAPResult::QUERY_ADD;
372 case LDAP_RES_DELETE:
373 ldap_result->type = LDAPResult::QUERY_DELETE;
375 case LDAP_RES_MODIFY:
376 ldap_result->type = LDAPResult::QUERY_MODIFY;
378 case LDAP_RES_SEARCH_RESULT:
379 // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
380 // then the result set is empty
381 ldap_result->type = LDAPResult::QUERY_SEARCH;
383 case LDAP_RES_COMPARE:
384 ldap_result->type = LDAPResult::QUERY_COMPARE;
392 case LDAP_RES_SEARCH_ENTRY:
394 BerElement* ber = NULL;
395 for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
397 berval** vals = ldap_get_values_len(this->con, cur, attr);
398 int count = ldap_count_values_len(vals);
400 std::vector<std::string> attrs;
401 for (int j = 0; j < count; ++j)
402 attrs.push_back(vals[j]->bv_val);
403 attributes[attr] = attrs;
405 ldap_value_free_len(vals);
415 case LDAP_RES_DELETE:
416 case LDAP_RES_MODIFY:
417 case LDAP_RES_COMPARE:
420 int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
421 if (parse_result != LDAP_SUCCESS)
423 ldap_result->error = ldap_err2string(parse_result);
427 if (cur_type == LDAP_RES_COMPARE)
429 if (errcode != LDAP_COMPARE_TRUE)
430 ldap_result->error = ldap_err2string(errcode);
432 else if (errcode != LDAP_SUCCESS)
433 ldap_result->error = ldap_err2string(errcode);
441 ldap_result->messages.push_back(attributes);
444 ldap_msgfree(result);
447 this->results.push_back(std::make_pair(i, ldap_result));
448 this->UnlockQueueWakeup();
450 this->NotifyParent();
454 void OnNotify() CXX11_OVERRIDE
456 LDAPService::result_queue r;
459 this->results.swap(r);
462 for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
464 LDAPInterface* li = i->first;
465 LDAPResult* res = i->second;
467 if (!res->error.empty())
477 class ModuleLDAP : public Module
479 typedef std::map<std::string, LDAPService*> ServiceMap;
480 ServiceMap LDAPServices;
483 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
487 ConfigTagList tags = ServerInstance->Config->ConfTags("database");
488 for (ConfigIter i = tags.first; i != tags.second; i++)
490 const reference<ConfigTag>& tag = i->second;
492 if (tag->getString("module") != "ldap")
495 std::string id = tag->getString("id");
497 ServiceMap::iterator curr = LDAPServices.find(id);
498 if (curr == LDAPServices.end())
500 LDAPService* conn = new LDAPService(this, tag);
503 ServerInstance->Modules->AddService(*conn);
504 ServerInstance->Threads->Start(conn);
509 LDAPServices.erase(curr);
513 for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
515 LDAPService* conn = i->second;
516 ServerInstance->Modules->DelService(*conn);
522 LDAPServices.swap(conns);
525 void OnUnloadModule(Module* m) CXX11_OVERRIDE
527 for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
529 LDAPService* s = it->second;
531 for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
533 int msgid = it2->first;
534 LDAPInterface* i = it2->second;
538 s->queries.erase(msgid);
540 for (unsigned int i = s->results.size(); i > 0; --i)
542 LDAPInterface* li = s->results[i - 1].first;
543 LDAPResult* r = s->results[i - 1].second;
545 if (li->creator == m)
547 s->results.erase(s->results.begin() + i - 1);
557 for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
559 LDAPService* conn = i->second;
566 Version GetVersion() CXX11_OVERRIDE
568 return Version("LDAP support", VF_VENDOR);
572 MODULE_INIT(ModuleLDAP)