]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ldap.cpp
6987381451f3b0690158219a1d786cb733bd063c
[user/henk/code/inspircd.git] / src / modules / extra / m_ldap.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013-2015 Adam <Adam@anope.org>
5  *   Copyright (C) 2003-2015 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, "libldap_r.lib")
27 # pragma comment(lib, "liblber.lib")
28 #endif
29
30 /* $LinkerFlags: -lldap_r -llber */
31
32 class LDAPService;
33
34 class LDAPRequest
35 {
36  public:
37         LDAPService* service;
38         LDAPInterface* inter;
39         LDAPMessage* message; /* message returned by ldap_ */
40         LDAPResult* result; /* final result */
41         struct timeval tv;
42         QueryType type;
43
44         LDAPRequest(LDAPService* s, LDAPInterface* i)
45                 : service(s)
46                 , inter(i)
47                 , message(NULL)
48                 , result(NULL)
49         {
50                 type = QUERY_UNKNOWN;
51                 tv.tv_sec = 0;
52                 tv.tv_usec = 100000;
53         }
54
55         virtual ~LDAPRequest()
56         {
57                 delete result;
58                 if (message != NULL)
59                         ldap_msgfree(message);
60         }
61
62         virtual int run() = 0;
63 };
64
65 class LDAPBind : public LDAPRequest
66 {
67         std::string who, pass;
68
69  public:
70         LDAPBind(LDAPService* s, LDAPInterface* i, const std::string& w, const std::string& p)
71                 : LDAPRequest(s, i)
72                 , who(w)
73                 , pass(p)
74         {
75                 type = QUERY_BIND;
76         }
77
78         int run() CXX11_OVERRIDE;
79 };
80
81 class LDAPSearch : public LDAPRequest
82 {
83         std::string base;
84         int searchscope;
85         std::string filter;
86
87  public:
88         LDAPSearch(LDAPService* s, LDAPInterface* i, const std::string& b, int se, const std::string& f)
89                 : LDAPRequest(s, i)
90                 , base(b)
91                 , searchscope(se)
92                 , filter(f)
93         {
94                 type = QUERY_SEARCH;
95         }
96
97         int run() CXX11_OVERRIDE;
98 };
99
100 class LDAPAdd : public LDAPRequest
101 {
102         std::string dn;
103         LDAPMods attributes;
104
105  public:
106         LDAPAdd(LDAPService* s, LDAPInterface* i, const std::string& d, const LDAPMods& attr)
107                 : LDAPRequest(s, i)
108                 , dn(d)
109                 , attributes(attr)
110         {
111                 type = QUERY_ADD;
112         }
113
114         int run() CXX11_OVERRIDE;
115 };
116
117 class LDAPDel : public LDAPRequest
118 {
119         std::string dn;
120
121  public:
122         LDAPDel(LDAPService* s, LDAPInterface* i, const std::string& d)
123                 : LDAPRequest(s, i)
124                 , dn(d)
125         {
126                 type = QUERY_DELETE;
127         }
128
129         int run() CXX11_OVERRIDE;
130 };
131
132 class LDAPModify : public LDAPRequest
133 {
134         std::string base;
135         LDAPMods attributes;
136
137  public:
138         LDAPModify(LDAPService* s, LDAPInterface* i, const std::string& b, const LDAPMods& attr)
139                 : LDAPRequest(s, i)
140                 , base(b)
141                 , attributes(attr)
142         {
143                 type = QUERY_MODIFY;
144         }
145
146         int run() CXX11_OVERRIDE;
147 };
148
149 class LDAPCompare : public LDAPRequest
150 {
151         std::string dn, attr, val;
152
153  public:
154         LDAPCompare(LDAPService* s, LDAPInterface* i, const std::string& d, const std::string& a, const std::string& v)
155                 : LDAPRequest(s, i)
156                 , dn(d)
157                 , attr(a)
158                 , val(v)
159         {
160                 type = QUERY_COMPARE;
161         }
162
163         int run() CXX11_OVERRIDE;
164 };
165
166 class LDAPService : public LDAPProvider, public SocketThread
167 {
168         LDAP* con;
169         reference<ConfigTag> config;
170         time_t last_connect;
171         int searchscope;
172         time_t timeout;
173
174  public:
175         static LDAPMod** BuildMods(const LDAPMods& attributes)
176         {
177                 LDAPMod** mods = new LDAPMod*[attributes.size() + 1];
178                 memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
179                 for (unsigned int x = 0; x < attributes.size(); ++x)
180                 {
181                         const LDAPModification& l = attributes[x];
182                         LDAPMod* mod = new LDAPMod;
183                         mods[x] = mod;
184
185                         if (l.op == LDAPModification::LDAP_ADD)
186                                 mod->mod_op = LDAP_MOD_ADD;
187                         else if (l.op == LDAPModification::LDAP_DEL)
188                                 mod->mod_op = LDAP_MOD_DELETE;
189                         else if (l.op == LDAPModification::LDAP_REPLACE)
190                                 mod->mod_op = LDAP_MOD_REPLACE;
191                         else if (l.op != 0)
192                         {
193                                 FreeMods(mods);
194                                 throw LDAPException("Unknown LDAP operation");
195                         }
196                         mod->mod_type = strdup(l.name.c_str());
197                         mod->mod_values = new char*[l.values.size() + 1];
198                         memset(mod->mod_values, 0, sizeof(char*) * (l.values.size() + 1));
199                         for (unsigned int j = 0, c = 0; j < l.values.size(); ++j)
200                                 if (!l.values[j].empty())
201                                         mod->mod_values[c++] = strdup(l.values[j].c_str());
202                 }
203                 return mods;
204         }
205
206         static void FreeMods(LDAPMod** mods)
207         {
208                 for (unsigned int i = 0; mods[i] != NULL; ++i)
209                 {
210                         LDAPMod* mod = mods[i];
211                         if (mod->mod_type != NULL)
212                                 free(mod->mod_type);
213                         if (mod->mod_values != NULL)
214                         {
215                                 for (unsigned int j = 0; mod->mod_values[j] != NULL; ++j)
216                                         free(mod->mod_values[j]);
217                                 delete[] mod->mod_values;
218                         }
219                 }
220                 delete[] mods;
221         }
222
223  private:
224         void Reconnect()
225         {
226                 // Only try one connect a minute. It is an expensive blocking operation
227                 if (last_connect > ServerInstance->Time() - 60)
228                         throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
229                 last_connect = ServerInstance->Time();
230
231                 ldap_unbind_ext(this->con, NULL, NULL);
232                 Connect();
233         }
234
235         void QueueRequest(LDAPRequest* r)
236         {
237                 this->LockQueue();
238                 this->queries.push_back(r);
239                 this->UnlockQueueWakeup();
240         }
241
242  public:
243         typedef std::vector<LDAPRequest*> query_queue;
244         query_queue queries, results;
245         Mutex process_mutex; /* held when processing requests not in either queue */
246
247         LDAPService(Module* c, ConfigTag* tag)
248                 : LDAPProvider(c, "LDAP/" + tag->getString("id"))
249                 , con(NULL), config(tag), last_connect(0)
250         {
251                 std::string scope = config->getString("searchscope");
252                 if (scope == "base")
253                         searchscope = LDAP_SCOPE_BASE;
254                 else if (scope == "onelevel")
255                         searchscope = LDAP_SCOPE_ONELEVEL;
256                 else
257                         searchscope = LDAP_SCOPE_SUBTREE;
258                 timeout = config->getInt("timeout", 5);
259
260                 Connect();
261         }
262
263         ~LDAPService()
264         {
265                 this->LockQueue();
266
267                 for (unsigned int i = 0; i < this->queries.size(); ++i)
268                 {
269                         LDAPRequest* req = this->queries[i];
270
271                         /* queries have no results yet */
272                         req->result = new LDAPResult();
273                         req->result->type = req->type;
274                         req->result->error = "LDAP Interface is going away";
275                         req->inter->OnError(*req->result);
276
277                         delete req;
278                 }
279                 this->queries.clear();
280
281                 for (unsigned int i = 0; i < this->results.size(); ++i)
282                 {
283                         LDAPRequest* req = this->results[i];
284
285                         /* even though this may have already finished successfully we return that it didn't */
286                         req->result->error = "LDAP Interface is going away";
287                         req->inter->OnError(*req->result);
288
289                         delete req;
290                 }
291                 this->results.clear();
292
293                 this->UnlockQueue();
294
295                 ldap_unbind_ext(this->con, NULL, NULL);
296         }
297
298         void Connect()
299         {
300                 std::string server = config->getString("server");
301                 int i = ldap_initialize(&this->con, server.c_str());
302                 if (i != LDAP_SUCCESS)
303                         throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
304
305                 const int version = LDAP_VERSION3;
306                 i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
307                 if (i != LDAP_OPT_SUCCESS)
308                 {
309                         ldap_unbind_ext(this->con, NULL, NULL);
310                         this->con = NULL;
311                         throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
312                 }
313
314                 const struct timeval tv = { 0, 0 };
315                 i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
316                 if (i != LDAP_OPT_SUCCESS)
317                 {
318                         ldap_unbind_ext(this->con, NULL, NULL);
319                         this->con = NULL;
320                         throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
321                 }
322         }
323
324         void BindAsManager(LDAPInterface* i) CXX11_OVERRIDE
325         {
326                 std::string binddn = config->getString("binddn");
327                 std::string bindauth = config->getString("bindauth");
328                 this->Bind(i, binddn, bindauth);
329         }
330
331         void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) CXX11_OVERRIDE
332         {
333                 LDAPBind* b = new LDAPBind(this, i, who, pass);
334                 QueueRequest(b);
335         }
336
337         void Search(LDAPInterface* i, const std::string& base, const std::string& filter) CXX11_OVERRIDE
338         {
339                 if (i == NULL)
340                         throw LDAPException("No interface");
341
342                 LDAPSearch* s = new LDAPSearch(this, i, base, searchscope, filter);
343                 QueueRequest(s);
344         }
345
346         void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) CXX11_OVERRIDE
347         {
348                 LDAPAdd* add = new LDAPAdd(this, i, dn, attributes);
349                 QueueRequest(add);
350         }
351
352         void Del(LDAPInterface* i, const std::string& dn) CXX11_OVERRIDE
353         {
354                 LDAPDel* del = new LDAPDel(this, i, dn);
355                 QueueRequest(del);
356         }
357
358         void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) CXX11_OVERRIDE
359         {
360                 LDAPModify* mod = new LDAPModify(this, i, base, attributes);
361                 QueueRequest(mod);
362         }
363
364         void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) CXX11_OVERRIDE
365         {
366                 LDAPCompare* comp = new LDAPCompare(this, i, dn, attr, val);
367                 QueueRequest(comp);
368         }
369
370  private:
371         void BuildReply(int res, LDAPRequest* req)
372         {
373                 LDAPResult* ldap_result = req->result = new LDAPResult();
374                 req->result->type = req->type;
375
376                 if (res != LDAP_SUCCESS)
377                 {
378                         ldap_result->error = ldap_err2string(res);
379                         return;
380                 }
381
382                 if (req->message == NULL)
383                 {
384                         return;
385                 }
386
387                 /* a search result */
388
389                 for (LDAPMessage* cur = ldap_first_message(this->con, req->message); cur; cur = ldap_next_message(this->con, cur))
390                 {
391                         LDAPAttributes attributes;
392
393                         char* dn = ldap_get_dn(this->con, cur);
394                         if (dn != NULL)
395                         {
396                                 attributes["dn"].push_back(dn);
397                                 ldap_memfree(dn);
398                                 dn = NULL;
399                         }
400
401                         BerElement* ber = NULL;
402
403                         for (char* attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
404                         {
405                                 berval** vals = ldap_get_values_len(this->con, cur, attr);
406                                 int count = ldap_count_values_len(vals);
407
408                                 std::vector<std::string> attrs;
409                                 for (int j = 0; j < count; ++j)
410                                         attrs.push_back(vals[j]->bv_val);
411                                 attributes[attr] = attrs;
412
413                                 ldap_value_free_len(vals);
414                                 ldap_memfree(attr);
415                         }
416                         if (ber != NULL)
417                                 ber_free(ber, 0);
418
419                         ldap_result->messages.push_back(attributes);
420                 }
421         }
422
423         void SendRequests()
424         {
425                 process_mutex.Lock();
426
427                 query_queue q;
428                 this->LockQueue();
429                 queries.swap(q);
430                 this->UnlockQueue();
431
432                 if (q.empty())
433                 {
434                         process_mutex.Unlock();
435                         return;
436                 }
437
438                 for (unsigned int i = 0; i < q.size(); ++i)
439                 {
440                         LDAPRequest* req = q[i];
441                         int ret = req->run();
442
443                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
444                         {
445                                 /* try again */
446                                 try
447                                 {
448                                         Reconnect();
449                                 }
450                                 catch (const LDAPException &)
451                                 {
452                                 }
453
454                                 ret = req->run();
455                         }
456
457                         BuildReply(ret, req);
458
459                         this->LockQueue();
460                         this->results.push_back(req);
461                         this->UnlockQueue();
462                 }
463
464                 this->NotifyParent();
465
466                 process_mutex.Unlock();
467         }
468
469  public:
470         void Run() CXX11_OVERRIDE
471         {
472                 while (!this->GetExitFlag())
473                 {
474                         this->LockQueue();
475                         if (this->queries.empty())
476                                 this->WaitForQueue();
477                         this->UnlockQueue();
478
479                         SendRequests();
480                 }
481         }
482
483         void OnNotify() CXX11_OVERRIDE
484         {
485                 query_queue r;
486
487                 this->LockQueue();
488                 this->results.swap(r);
489                 this->UnlockQueue();
490
491                 for (unsigned int i = 0; i < r.size(); ++i)
492                 {
493                         LDAPRequest* req = r[i];
494                         LDAPInterface* li = req->inter;
495                         LDAPResult* res = req->result;
496
497                         if (!res->error.empty())
498                                 li->OnError(*res);
499                         else
500                                 li->OnResult(*res);
501
502                         delete req;
503                 }
504         }
505
506         LDAP* GetConnection()
507         {
508                 return con;
509         }
510 };
511
512 class ModuleLDAP : public Module
513 {
514         typedef insp::flat_map<std::string, LDAPService*> ServiceMap;
515         ServiceMap LDAPServices;
516
517  public:
518         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
519         {
520                 ServiceMap conns;
521
522                 ConfigTagList tags = ServerInstance->Config->ConfTags("database");
523                 for (ConfigIter i = tags.first; i != tags.second; i++)
524                 {
525                         const reference<ConfigTag>& tag = i->second;
526
527                         if (tag->getString("module") != "ldap")
528                                 continue;
529
530                         std::string id = tag->getString("id");
531
532                         ServiceMap::iterator curr = LDAPServices.find(id);
533                         if (curr == LDAPServices.end())
534                         {
535                                 LDAPService* conn = new LDAPService(this, tag);
536                                 conns[id] = conn;
537
538                                 ServerInstance->Modules->AddService(*conn);
539                                 ServerInstance->Threads.Start(conn);
540                         }
541                         else
542                         {
543                                 conns.insert(*curr);
544                                 LDAPServices.erase(curr);
545                         }
546                 }
547
548                 for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
549                 {
550                         LDAPService* conn = i->second;
551                         ServerInstance->Modules->DelService(*conn);
552                         conn->join();
553                         conn->OnNotify();
554                         delete conn;
555                 }
556
557                 LDAPServices.swap(conns);
558         }
559
560         void OnUnloadModule(Module* m) CXX11_OVERRIDE
561         {
562                 for (ServiceMap::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
563                 {
564                         LDAPService* s = it->second;
565
566                         s->process_mutex.Lock();
567                         s->LockQueue();
568
569                         for (unsigned int i = s->queries.size(); i > 0; --i)
570                         {
571                                 LDAPRequest* req = s->queries[i - 1];
572                                 LDAPInterface* li = req->inter;
573
574                                 if (li->creator == m)
575                                 {
576                                         s->queries.erase(s->queries.begin() + i - 1);
577                                         delete req;
578                                 }
579                         }
580
581                         for (unsigned int i = s->results.size(); i > 0; --i)
582                         {
583                                 LDAPRequest* req = s->results[i - 1];
584                                 LDAPInterface* li = req->inter;
585
586                                 if (li->creator == m)
587                                 {
588                                         s->results.erase(s->results.begin() + i - 1);
589                                         delete req;
590                                 }
591                         }
592
593                         s->UnlockQueue();
594                         s->process_mutex.Unlock();
595                 }
596         }
597
598         ~ModuleLDAP()
599         {
600                 for (ServiceMap::iterator i = LDAPServices.begin(); i != LDAPServices.end(); ++i)
601                 {
602                         LDAPService* conn = i->second;
603                         conn->join();
604                         conn->OnNotify();
605                         delete conn;
606                 }
607         }
608
609         Version GetVersion() CXX11_OVERRIDE
610         {
611                 return Version("LDAP support", VF_VENDOR);
612         }
613 };
614
615 int LDAPBind::run()
616 {
617         berval cred;
618         cred.bv_val = strdup(pass.c_str());
619         cred.bv_len = pass.length();
620
621         int i = ldap_sasl_bind_s(service->GetConnection(), who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
622
623         free(cred.bv_val);
624
625         return i;
626 }
627
628 int LDAPSearch::run()
629 {
630         return ldap_search_ext_s(service->GetConnection(), base.c_str(), searchscope, filter.c_str(), NULL, 0, NULL, NULL, &tv, 0, &message);
631 }
632
633 int LDAPAdd::run()
634 {
635         LDAPMod** mods = LDAPService::BuildMods(attributes);
636         int i = ldap_add_ext_s(service->GetConnection(), dn.c_str(), mods, NULL, NULL);
637         LDAPService::FreeMods(mods);
638         return i;
639 }
640
641 int LDAPDel::run()
642 {
643         return ldap_delete_ext_s(service->GetConnection(), dn.c_str(), NULL, NULL);
644 }
645
646 int LDAPModify::run()
647 {
648         LDAPMod** mods = LDAPService::BuildMods(attributes);
649         int i = ldap_modify_ext_s(service->GetConnection(), base.c_str(), mods, NULL, NULL);
650         LDAPService::FreeMods(mods);
651         return i;
652 }
653
654 int LDAPCompare::run()
655 {
656         berval cred;
657         cred.bv_val = strdup(val.c_str());
658         cred.bv_len = val.length();
659
660         int ret = ldap_compare_ext_s(service->GetConnection(), dn.c_str(), attr.c_str(), &cred, NULL, NULL);
661
662         free(cred.bv_val);
663
664         return ret;
665
666 }
667
668 MODULE_INIT(ModuleLDAP)