]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ldap.cpp
Add m_ldap, and convert m_ldapoper and m_ldapauth to use it.
[user/henk/code/inspircd.git] / src / modules / extra / m_ldap.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013 Adam <Adam@anope.org>
5  *   Copyright (C) 2003-2013 Anope Team <team@anope.org>
6  *
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.
10  *
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
14  * details.
15  *
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/>.
18  */
19
20 #include "inspircd.h"
21 #include "modules/ldap.h"
22
23 #include <ldap.h>
24
25 #ifdef _WIN32
26 # pragma comment(lib, "ldap.lib")
27 # pragma comment(lib, "lber.lib")
28 #endif
29
30 /* $LinkerFlags: -lldap */
31
32 class LDAPService : public LDAPProvider, public SocketThread
33 {
34         LDAP* con;
35         reference<ConfigTag> config;
36         time_t last_connect;
37         int searchscope;
38
39         LDAPMod** BuildMods(const LDAPMods& attributes)
40         {
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)
44                 {
45                         const LDAPModification& l = attributes[x];
46                         LDAPMod* mod = new LDAPMod;
47                         mods[x] = mod;
48
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;
55                         else if (l.op != 0)
56                         {
57                                 FreeMods(mods);
58                                 throw LDAPException("Unknown LDAP operation");
59                         }
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());
66                 }
67                 return mods;
68         }
69
70         void FreeMods(LDAPMod** mods)
71         {
72                 for (unsigned int i = 0; mods[i] != NULL; ++i)
73                 {
74                         LDAPMod* mod = mods[i];
75                         if (mod->mod_type != NULL)
76                                 free(mod->mod_type);
77                         if (mod->mod_values != NULL)
78                         {
79                                 for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
80                                         free(mod->mod_values[j]);
81                                 delete[] mod->mod_values;
82                         }
83                 }
84                 delete[] mods;
85         }
86
87         void Reconnect()
88         {
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();
93
94                 ldap_unbind_ext(this->con, NULL, NULL);
95                 Connect();
96         }
97
98         void SaveInterface(LDAPInterface* i, LDAPQuery msgid)
99         {
100                 if (i != NULL)
101                 {
102                         this->LockQueue();
103                         this->queries[msgid] = i;
104                         this->UnlockQueueWakeup();
105                 }
106         }
107
108  public:
109         typedef std::map<int, LDAPInterface*> query_queue;
110         typedef std::vector<std::pair<LDAPInterface*, LDAPResult*> > result_queue;
111         query_queue queries;
112         result_queue results;
113
114         LDAPService(Module* c, ConfigTag* tag)
115                 : LDAPProvider(c, "LDAP/" + tag->getString("id"))
116                 , con(NULL), config(tag), last_connect(0)
117         {
118                 std::string scope = config->getString("searchscope");
119                 if (scope == "base")
120                         searchscope = LDAP_SCOPE_BASE;
121                 else if (scope == "onelevel")
122                         searchscope = LDAP_SCOPE_ONELEVEL;
123                 else
124                         searchscope = LDAP_SCOPE_SUBTREE;
125
126                 Connect();
127         }
128
129         ~LDAPService()
130         {
131                 this->LockQueue();
132
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();
136
137                 for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i)
138                 {
139                         i->second->error = "LDAP Interface is going away";
140                         i->first->OnError(*i->second);
141                 }
142                 this->results.clear();
143
144                 this->UnlockQueue();
145
146                 ldap_unbind_ext(this->con, NULL, NULL);
147         }
148
149         void Connect()
150         {
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)
158                 {
159                         ldap_unbind_ext(this->con, NULL, NULL);
160                         this->con = NULL;
161                         throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
162                 }
163         }
164
165         LDAPQuery BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
166         {
167                 std::string binddn = config->getString("binddn");
168                 std::string bindauth = config->getString("bindauth");
169                 return this->Bind(i, binddn, bindauth);
170         }
171
172         LDAPQuery Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
173         {
174                 berval cred;
175                 cred.bv_val = strdup(pass.c_str());
176                 cred.bv_len = pass.length();
177
178                 LDAPQuery msgid;
179                 int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
180                 free(cred.bv_val);
181                 if (ret != LDAP_SUCCESS)
182                 {
183                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
184                         {
185                                 this->Reconnect();
186                                 return this->Bind(i, who, pass);
187                         }
188                         else
189                                 throw LDAPException(ldap_err2string(ret));
190                 }
191
192                 SaveInterface(i, msgid);
193                 return msgid;
194         }
195
196         LDAPQuery Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
197         {
198                 if (i == NULL)
199                         throw LDAPException("No interface");
200
201                 LDAPQuery msgid;
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)
204                 {
205                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
206                         {
207                                 this->Reconnect();
208                                 return this->Search(i, base, filter);
209                         }
210                         else
211                                 throw LDAPException(ldap_err2string(ret));
212                 }
213
214                 SaveInterface(i, msgid);
215                 return msgid;
216         }
217
218         LDAPQuery Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
219         {
220                 LDAPMod** mods = this->BuildMods(attributes);
221                 LDAPQuery msgid;
222                 int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
223                 this->FreeMods(mods);
224
225                 if (ret != LDAP_SUCCESS)
226                 {
227                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
228                         {
229                                 this->Reconnect();
230                                 return this->Add(i, dn, attributes);
231                         }
232                         else
233                                 throw LDAPException(ldap_err2string(ret));
234                 }
235
236                 SaveInterface(i, msgid);
237                 return msgid;
238         }
239
240         LDAPQuery Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
241         {
242                 LDAPQuery msgid;
243                 int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
244
245                 if (ret != LDAP_SUCCESS)
246                 {
247                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
248                         {
249                                 this->Reconnect();
250                                 return this->Del(i, dn);
251                         }
252                         else
253                                 throw LDAPException(ldap_err2string(ret));
254                 }
255
256                 SaveInterface(i, msgid);
257                 return msgid;
258         }
259
260         LDAPQuery Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
261         {
262                 LDAPMod** mods = this->BuildMods(attributes);
263                 LDAPQuery msgid;
264                 int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
265                 this->FreeMods(mods);
266
267                 if (ret != LDAP_SUCCESS)
268                 {
269                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
270                         {
271                                 this->Reconnect();
272                                 return this->Modify(i, base, attributes);
273                         }
274                         else
275                                 throw LDAPException(ldap_err2string(ret));
276                 }
277
278                 SaveInterface(i, msgid);
279                 return msgid;
280         }
281
282         LDAPQuery Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
283         {
284                 berval cred;
285                 cred.bv_val = strdup(val.c_str());
286                 cred.bv_len = val.length();
287
288                 LDAPQuery msgid;
289                 int ret = ldap_compare_ext(con, dn.c_str(), attr.c_str(), &cred, NULL, NULL, &msgid);
290                 free(cred.bv_val);
291
292                 if (ret != LDAP_SUCCESS)
293                 {
294                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
295                         {
296                                 this->Reconnect();
297                                 return this->Compare(i, dn, attr, val);
298                         }
299                         else
300                                 throw LDAPException(ldap_err2string(ret));
301                 }
302
303                 SaveInterface(i, msgid);
304                 return msgid;
305         }
306
307         void Run() CXX11_OVERRIDE
308         {
309                 while (!this->GetExitFlag())
310                 {
311                         this->LockQueue();
312                         if (this->queries.empty())
313                         {
314                                 this->WaitForQueue();
315                                 this->UnlockQueue();
316                                 continue;
317                         }
318                         else
319                                 this->UnlockQueue();
320
321                         struct timeval tv = { 1, 0 };
322                         LDAPMessage* result;
323                         int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
324                         if (rtype <= 0 || this->GetExitFlag())
325                                 continue;
326
327                         int cur_id = ldap_msgid(result);
328
329                         this->LockQueue();
330
331                         query_queue::iterator it = this->queries.find(cur_id);
332                         if (it == this->queries.end())
333                         {
334                                 this->UnlockQueue();
335                                 ldap_msgfree(result);
336                                 continue;
337                         }
338                         LDAPInterface* i = it->second;
339                         this->queries.erase(it);
340
341                         this->UnlockQueue();
342
343                         LDAPResult* ldap_result = new LDAPResult();
344                         ldap_result->id = cur_id;
345
346                         for (LDAPMessage* cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
347                         {
348                                 int cur_type = ldap_msgtype(cur);
349
350                                 LDAPAttributes attributes;
351
352                                 {
353                                         char* dn = ldap_get_dn(this->con, cur);
354                                         if (dn != NULL)
355                                         {
356                                                 attributes["dn"].push_back(dn);
357                                                 ldap_memfree(dn);
358                                         }
359                                 }
360
361                                 switch (cur_type)
362                                 {
363                                         case LDAP_RES_BIND:
364                                                 ldap_result->type = LDAPResult::QUERY_BIND;
365                                                 break;
366                                         case LDAP_RES_SEARCH_ENTRY:
367                                                 ldap_result->type = LDAPResult::QUERY_SEARCH;
368                                                 break;
369                                         case LDAP_RES_ADD:
370                                                 ldap_result->type = LDAPResult::QUERY_ADD;
371                                                 break;
372                                         case LDAP_RES_DELETE:
373                                                 ldap_result->type = LDAPResult::QUERY_DELETE;
374                                                 break;
375                                         case LDAP_RES_MODIFY:
376                                                 ldap_result->type = LDAPResult::QUERY_MODIFY;
377                                                 break;
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;
382                                                 break;
383                                         case LDAP_RES_COMPARE:
384                                                 ldap_result->type = LDAPResult::QUERY_COMPARE;
385                                                 break;
386                                         default:
387                                                 continue;
388                                 }
389
390                                 switch (cur_type)
391                                 {
392                                         case LDAP_RES_SEARCH_ENTRY:
393                                         {
394                                                 BerElement* ber = NULL;
395                                                 for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
396                                                 {
397                                                         berval** vals = ldap_get_values_len(this->con, cur, attr);
398                                                         int count = ldap_count_values_len(vals);
399
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;
404
405                                                         ldap_value_free_len(vals);
406                                                         ldap_memfree(attr);
407                                                 }
408                                                 if (ber != NULL)
409                                                         ber_free(ber, 0);
410
411                                                 break;
412                                         }
413                                         case LDAP_RES_BIND:
414                                         case LDAP_RES_ADD:
415                                         case LDAP_RES_DELETE:
416                                         case LDAP_RES_MODIFY:
417                                         case LDAP_RES_COMPARE:
418                                         {
419                                                 int errcode = -1;
420                                                 int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
421                                                 if (parse_result != LDAP_SUCCESS)
422                                                 {
423                                                         ldap_result->error = ldap_err2string(parse_result);
424                                                 }
425                                                 else
426                                                 {
427                                                         if (cur_type == LDAP_RES_COMPARE)
428                                                         {
429                                                                 if (errcode != LDAP_COMPARE_TRUE)
430                                                                         ldap_result->error = ldap_err2string(errcode);
431                                                         }
432                                                         else if (errcode != LDAP_SUCCESS)
433                                                                 ldap_result->error = ldap_err2string(errcode);
434                                                 }
435                                                 break;
436                                         }
437                                         default:
438                                                 continue;
439                                 }
440
441                                 ldap_result->messages.push_back(attributes);
442                         }
443
444                         ldap_msgfree(result);
445
446                         this->LockQueue();
447                         this->results.push_back(std::make_pair(i, ldap_result));
448                         this->UnlockQueueWakeup();
449
450                         this->NotifyParent();
451                 }
452         }
453
454         void OnNotify() CXX11_OVERRIDE
455         {
456                 LDAPService::result_queue r;
457
458                 this->LockQueue();
459                 this->results.swap(r);
460                 this->UnlockQueue();
461
462                 for (LDAPService::result_queue::iterator i = r.begin(); i != r.end(); ++i)
463                 {
464                         LDAPInterface* li = i->first;
465                         LDAPResult* res = i->second;
466
467                         if (!res->error.empty())
468                                 li->OnError(*res);
469                         else
470                                 li->OnResult(*res);
471
472                         delete res;
473                 }
474         }
475 };
476
477 class ModuleLDAP : public Module
478 {
479         typedef std::map<std::string, LDAPService*> ServiceMap;
480         ServiceMap LDAPServices;
481
482  public:
483         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
484         {
485                 ServiceMap conns;
486
487                 ConfigTagList tags = ServerInstance->Config->ConfTags("database");
488                 for (ConfigIter i = tags.first; i != tags.second; i++)
489                 {
490                         const reference<ConfigTag>& tag = i->second;
491
492                         if (tag->getString("module") != "ldap")
493                                 continue;
494
495                         std::string id = tag->getString("id");
496
497                         ServiceMap::iterator curr = LDAPServices.find(id);
498                         if (curr == LDAPServices.end())
499                         {
500                                 LDAPService* conn = new LDAPService(this, tag);
501                                 conns[id] = conn;
502
503                                 ServerInstance->Modules->AddService(*conn);
504                                 ServerInstance->Threads->Start(conn);
505                         }
506                         else
507                         {
508                                 conns.insert(*curr);
509                                 LDAPServices.erase(curr);
510                         }
511                 }
512
513                 for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
514                 {
515                         LDAPService* conn = i->second;
516                         ServerInstance->Modules->DelService(*conn);
517                         conn->join();
518                         conn->OnNotify();
519                         delete conn;
520                 }
521
522                 LDAPServices.swap(conns);
523         }
524
525         void OnUnloadModule(Module* m) CXX11_OVERRIDE
526         {
527                 for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
528                 {
529                         LDAPService* s = it->second;
530                         s->LockQueue();
531                         for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
532                         {
533                                 int msgid = it2->first;
534                                 LDAPInterface* i = it2->second;
535                                 ++it2;
536
537                                 if (i->creator == m)
538                                         s->queries.erase(msgid);
539                         }
540                         for (unsigned int i = s->results.size(); i > 0; --i)
541                         {
542                                 LDAPInterface* li = s->results[i - 1].first;
543                                 LDAPResult* r = s->results[i - 1].second;
544
545                                 if (li->creator == m)
546                                 {
547                                         s->results.erase(s->results.begin() + i - 1);
548                                         delete r;
549                                 }
550                         }
551                         s->UnlockQueue();
552                 }
553         }
554
555         ~ModuleLDAP()
556         {
557                 for (std::map<std::string, LDAPService*>::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
558                 {
559                         LDAPService* conn = i->second;
560                         conn->join();
561                         conn->OnNotify();
562                         delete conn;
563                 }
564         }
565
566         Version GetVersion() CXX11_OVERRIDE
567         {
568                 return Version("LDAP support", VF_VENDOR);
569         }
570 };
571
572 MODULE_INIT(ModuleLDAP)