Anope IRC Services  Version 2.0
m_ldap.cpp
Go to the documentation of this file.
1 /* RequiredLibraries: ldap,lber */
2 /* RequiredWindowsLibraries: libldap,liblber */
3 
4 #include "module.h"
5 #include "modules/ldap.h"
6 #include <ldap.h>
7 
8 static Pipe *me;
9 
10 class LDAPService : public LDAPProvider, public Thread, public Condition
11 {
13  int port;
16  time_t timeout;
17 
18  LDAP *con;
19 
20  time_t last_connect;
21 
22  LDAPMod **BuildMods(const LDAPMods &attributes)
23  {
24  LDAPMod **mods = new LDAPMod*[attributes.size() + 1];
25  memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
26  for (unsigned x = 0; x < attributes.size(); ++x)
27  {
28  const LDAPModification &l = attributes[x];
29  mods[x] = new LDAPMod();
30 
32  mods[x]->mod_op = LDAP_MOD_ADD;
33  else if (l.op == LDAPModification::LDAP_DEL)
34  mods[x]->mod_op = LDAP_MOD_DELETE;
35  else if (l.op == LDAPModification::LDAP_REPLACE)
36  mods[x]->mod_op = LDAP_MOD_REPLACE;
37  else if (l.op != 0)
38  throw LDAPException("Unknown LDAP operation");
39  mods[x]->mod_type = strdup(l.name.c_str());
40  mods[x]->mod_values = new char*[l.values.size() + 1];
41  memset(mods[x]->mod_values, 0, sizeof(char *) * (l.values.size() + 1));
42  for (unsigned j = 0, c = 0; j < l.values.size(); ++j)
43  if (!l.values[j].empty())
44  mods[x]->mod_values[c++] = strdup(l.values[j].c_str());
45  }
46  return mods;
47  }
48 
49  void FreeMods(LDAPMod **mods)
50  {
51  for (int i = 0; mods[i] != NULL; ++i)
52  {
53  free(mods[i]->mod_type);
54  for (int j = 0; mods[i]->mod_values[j] != NULL; ++j)
55  free(mods[i]->mod_values[j]);
56  delete [] mods[i]->mod_values;
57  }
58  delete [] mods;
59  }
60 
61  void Reconnect()
62  {
63  /* Only try one connect a minute. It is an expensive blocking operation */
64  if (last_connect > Anope::CurTime - 60)
65  throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
66  last_connect = Anope::CurTime;
67 
68  ldap_unbind_ext(this->con, NULL, NULL);
69  int i = ldap_initialize(&this->con, this->server.c_str());
70  if (i != LDAP_SUCCESS)
71  throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
72  }
73 
74  public:
75  typedef std::map<LDAPQuery, std::pair<time_t, LDAPInterface *> > query_queue;
76  typedef std::vector<std::pair<LDAPInterface *, LDAPResult *> > result_queue;
77  query_queue queries;
78  result_queue results;
79 
80  LDAPService(Module *o, const Anope::string &n, const Anope::string &s, int po, const Anope::string &b, const Anope::string &p, time_t t) : LDAPProvider(o, n), server(s), port(po), admin_binddn(b), admin_pass(p), timeout(t), last_connect(0)
81  {
82  int i = ldap_initialize(&this->con, this->server.c_str());
83  if (i != LDAP_SUCCESS)
84  throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
85 
86  const int version = LDAP_VERSION3;
87  i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
88  if (i != LDAP_OPT_SUCCESS)
89  throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
90 
91  const struct timeval tv = { 0, 0 };
92  i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv);
93  if (i != LDAP_OPT_SUCCESS)
94  throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i));
95  }
96 
98  {
99  this->Lock();
100 
101  for (query_queue::iterator it = this->queries.begin(), it_end = this->queries.end(); it != it_end; ++it)
102  {
103  LDAPQuery msgid = it->first;
104  LDAPInterface *i = it->second.second;
105 
106  ldap_abandon_ext(this->con, msgid, NULL, NULL);
107  if (i)
108  i->OnDelete();
109  }
110  this->queries.clear();
111 
112  for (result_queue::iterator it = this->results.begin(), it_end = this->results.end(); it != it_end; ++it)
113  {
114  LDAPInterface *i = it->first;
115  LDAPResult *r = it->second;
116 
117  r->error = "LDAP Interface is going away";
118  if (i)
119  i->OnError(*r);
120 
121  delete r;
122  }
123  this->results.clear();
124 
125  this->Unlock();
126 
127  ldap_unbind_ext(this->con, NULL, NULL);
128  }
129 
131  {
132  return this->Bind(i, this->admin_binddn, this->admin_pass);
133  }
134 
136  {
137  berval cred;
138  cred.bv_val = strdup(pass.c_str());
139  cred.bv_len = pass.length();
140 
141  LDAPQuery msgid;
142  int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
143  free(cred.bv_val);
144  if (ret != LDAP_SUCCESS)
145  {
146  if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
147  {
148  this->Reconnect();
149  return this->Bind(i, who, pass);
150  }
151  else
152  throw LDAPException(ldap_err2string(ret));
153  }
154 
155  if (i != NULL)
156  {
157  this->Lock();
158  this->queries[msgid] = std::make_pair(Anope::CurTime, i);
159  this->Unlock();
160  }
161  this->Wakeup();
162 
163  return msgid;
164  }
165 
167  {
168  if (i == NULL)
169  throw LDAPException("No interface");
170 
171  LDAPQuery msgid;
172  int ret = ldap_search_ext(this->con, base.c_str(), LDAP_SCOPE_SUBTREE, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
173  if (ret != LDAP_SUCCESS)
174  {
175  if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
176  {
177  this->Reconnect();
178  return this->Search(i, base, filter);
179  }
180  else
181  throw LDAPException(ldap_err2string(ret));
182  }
183 
184  this->Lock();
185  this->queries[msgid] = std::make_pair(Anope::CurTime, i);
186  this->Unlock();
187  this->Wakeup();
188 
189  return msgid;
190  }
191 
193  {
194  LDAPMod **mods = this->BuildMods(attributes);
195  LDAPQuery msgid;
196  int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
197  this->FreeMods(mods);
198 
199  if (ret != LDAP_SUCCESS)
200  {
201  if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
202  {
203  this->Reconnect();
204  return this->Add(i, dn, attributes);
205  }
206  else
207  throw LDAPException(ldap_err2string(ret));
208  }
209 
210  if (i != NULL)
211  {
212  this->Lock();
213  this->queries[msgid] = std::make_pair(Anope::CurTime, i);
214  this->Unlock();
215  }
216  this->Wakeup();
217 
218  return msgid;
219  }
220 
222  {
223  LDAPQuery msgid;
224  int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
225 
226  if (ret != LDAP_SUCCESS)
227  {
228  if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
229  {
230  this->Reconnect();
231  return this->Del(i, dn);
232  }
233  else
234  throw LDAPException(ldap_err2string(ret));
235  }
236 
237  if (i != NULL)
238  {
239  this->Lock();
240  this->queries[msgid] = std::make_pair(Anope::CurTime, i);
241  this->Unlock();
242  }
243  this->Wakeup();
244 
245  return msgid;
246  }
247 
249  {
250  LDAPMod **mods = this->BuildMods(attributes);
251  LDAPQuery msgid;
252  int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
253  this->FreeMods(mods);
254 
255  if (ret != LDAP_SUCCESS)
256  {
257  if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
258  {
259  this->Reconnect();
260  return this->Modify(i, base, attributes);
261  }
262  else
263  throw LDAPException(ldap_err2string(ret));
264  }
265 
266  if (i != NULL)
267  {
268  this->Lock();
269  this->queries[msgid] = std::make_pair(Anope::CurTime, i);
270  this->Unlock();
271  }
272  this->Wakeup();
273 
274  return msgid;
275  }
276 
277  private:
278  void Timeout()
279  {
280  this->Lock();
281  for (query_queue::iterator it = this->queries.begin(), it_end = this->queries.end(); it != it_end;)
282  {
283  LDAPQuery msgid = it->first;
284  time_t created = it->second.first;
285  LDAPInterface *i = it->second.second;
286  ++it;
287 
288  if (Anope::CurTime > created + timeout)
289  {
290  LDAPResult *ldap_result = new LDAPResult();
291  ldap_result->id = msgid;
292  ldap_result->error = "Query timed out";
293 
294  this->queries.erase(msgid);
295  this->results.push_back(std::make_pair(i, ldap_result));
296 
297  me->Notify();
298  }
299  }
300  this->Unlock();
301  }
302 
303  public:
305  {
306  while (!this->GetExitState())
307  {
308  if (this->queries.empty())
309  {
310  this->Lock();
311  this->Wait();
312  this->Unlock();
313  continue;
314  }
315  else
316  this->Timeout();
317 
318  struct timeval tv = { 1, 0 };
319  LDAPMessage *result;
320  int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
321  if (rtype <= 0)
322  continue;
323 
324  int cur_id = ldap_msgid(result);
325 
326  this->Lock();
327 
328  query_queue::iterator it = this->queries.find(cur_id);
329  if (it == this->queries.end())
330  {
331  this->Unlock();
332  ldap_msgfree(result);
333  continue;
334  }
335  LDAPInterface *i = it->second.second;
336  this->queries.erase(it);
337 
338  this->Unlock();
339 
340  LDAPResult *ldap_result = new LDAPResult();
341  ldap_result->id = cur_id;
342 
343  for (LDAPMessage *cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
344  {
345  int cur_type = ldap_msgtype(cur);
346 
347  LDAPAttributes attributes;
348 
349  char *dn = ldap_get_dn(this->con, cur);
350  if (dn != NULL)
351  {
352  attributes["dn"].push_back(dn);
353  ldap_memfree(dn);
354  dn = NULL;
355  }
356 
357  switch (cur_type)
358  {
359  case LDAP_RES_BIND:
360  ldap_result->type = LDAPResult::QUERY_BIND;
361  break;
362  case LDAP_RES_SEARCH_ENTRY:
363  ldap_result->type = LDAPResult::QUERY_SEARCH;
364  break;
365  case LDAP_RES_ADD:
366  ldap_result->type = LDAPResult::QUERY_ADD;
367  break;
368  case LDAP_RES_DELETE:
369  ldap_result->type = LDAPResult::QUERY_DELETE;
370  break;
371  case LDAP_RES_MODIFY:
372  ldap_result->type = LDAPResult::QUERY_MODIFY;
373  break;
374  case LDAP_RES_SEARCH_RESULT:
375  // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
376  // then the result set is empty
377  ldap_result->type = LDAPResult::QUERY_SEARCH;
378  break;
379  default:
380  Log(LOG_DEBUG) << "m_ldap: Unknown msg type " << cur_type;
381  continue;
382  }
383 
384  switch (cur_type)
385  {
386  case LDAP_RES_BIND:
387  {
388  int errcode = -1;
389  int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
390  if (parse_result != LDAP_SUCCESS)
391  ldap_result->error = ldap_err2string(parse_result);
392  else if (errcode != LDAP_SUCCESS)
393  ldap_result->error = ldap_err2string(errcode);
394  break;
395  }
396  case LDAP_RES_SEARCH_ENTRY:
397  {
398  BerElement *ber = NULL;
399  for (char *attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
400  {
401  berval **vals = ldap_get_values_len(this->con, cur, attr);
402  int count = ldap_count_values_len(vals);
403 
404  std::vector<Anope::string> attrs;
405  for (int j = 0; j < count; ++j)
406  attrs.push_back(vals[j]->bv_val);
407  attributes[attr] = attrs;
408 
409  ldap_value_free_len(vals);
410  ldap_memfree(attr);
411  }
412  if (ber != NULL)
413  ber_free(ber, 0);
414 
415  break;
416  }
417  case LDAP_RES_ADD:
418  case LDAP_RES_DELETE:
419  case LDAP_RES_MODIFY:
420  {
421  int errcode = -1;
422  int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
423  if (parse_result != LDAP_SUCCESS)
424  ldap_result->error = ldap_err2string(parse_result);
425  else if (errcode != LDAP_SUCCESS)
426  ldap_result->error = ldap_err2string(errcode);
427  break;
428  }
429  default:
430  continue;
431  }
432 
433  ldap_result->messages.push_back(attributes);
434  }
435 
436  ldap_msgfree(result);
437 
438  this->Lock();
439  this->results.push_back(std::make_pair(i, ldap_result));
440  this->Unlock();
441 
442  me->Notify();
443  }
444  }
445 };
446 
447 class ModuleLDAP : public Module, public Pipe
448 {
449  std::map<Anope::string, LDAPService *> LDAPServices;
450  public:
451 
452  ModuleLDAP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
453  {
454  me = this;
455 
456  }
457 
459  {
460  for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
461  {
462  it->second->SetExitState();
463  it->second->Wakeup();
464  it->second->Join();
465  delete it->second;
466  }
467  LDAPServices.clear();
468  }
469 
471  {
472  Configuration::Block *conf = config->GetModule(this);
473 
474  for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end();)
475  {
476  const Anope::string &cname = it->first;
477  LDAPService *s = it->second;
478  int i;
479 
480  ++it;
481 
482  for (i = 0; i < conf->CountBlock("ldap"); ++i)
483  if (conf->GetBlock("ldap", i)->Get<const Anope::string>("name", "ldap/main") == cname)
484  break;
485 
486  if (i == conf->CountBlock("ldap"))
487  {
488  Log(LOG_NORMAL, "ldap") << "LDAP: Removing server connection " << cname;
489 
490  s->SetExitState();
491  s->Wakeup();
492  this->LDAPServices.erase(cname);
493  }
494  }
495 
496  for (int i = 0; i < conf->CountBlock("ldap"); ++i)
497  {
498  Configuration::Block *ldap = conf->GetBlock("ldap", i);
499 
500  const Anope::string &connname = ldap->Get<const Anope::string>("name", "ldap/main");
501 
502  if (this->LDAPServices.find(connname) == this->LDAPServices.end())
503  {
504  const Anope::string &server = ldap->Get<const Anope::string>("server", "127.0.0.1");
505  int port = ldap->Get<int>("port", "389");
506  const Anope::string &admin_binddn = ldap->Get<const Anope::string>("admin_binddn");
507  const Anope::string &admin_password = ldap->Get<const Anope::string>("admin_password");
508  time_t timeout = ldap->Get<time_t>("timeout", "5");
509 
510  try
511  {
512  LDAPService *ss = new LDAPService(this, connname, server, port, admin_binddn, admin_password, timeout);
513  ss->Start();
514  this->LDAPServices.insert(std::make_pair(connname, ss));
515 
516  Log(LOG_NORMAL, "ldap") << "LDAP: Successfully connected to server " << connname << " (" << server << ")";
517  }
518  catch (const LDAPException &ex)
519  {
520  Log(LOG_NORMAL, "ldap") << "LDAP: " << ex.GetReason();
521  }
522  }
523  }
524  }
525 
527  {
528  for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
529  {
530  LDAPService *s = it->second;
531  s->Lock();
532  for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
533  {
534  LDAPQuery msgid = it2->first;
535  LDAPInterface *i = it2->second.second;
536  ++it2;
537 
538  if (i && i->owner == m)
539  {
540  i->OnDelete();
541  s->queries.erase(msgid);
542  }
543  }
544  for (unsigned i = s->results.size(); i > 0; --i)
545  {
546  LDAPInterface *li = s->results[i - 1].first;
547  LDAPResult *r = s->results[i - 1].second;
548 
549  if (li && li->owner == m)
550  {
551  s->results.erase(s->results.begin() + i - 1);
552  delete r;
553  }
554  }
555  s->Unlock();
556  }
557  }
558 
560  {
561  for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
562  {
563  LDAPService *s = it->second;
564 
566  s->Lock();
567  results.swap(s->results);
568  s->Unlock();
569 
570  for (unsigned i = 0; i < results.size(); ++i)
571  {
572  LDAPInterface *li = results[i].first;
573  LDAPResult *r = results[i].second;
574 
575  if (li != NULL)
576  {
577  if (!r->getError().empty())
578  {
579  Log(this) << "Error running LDAP query: " << r->getError();
580  li->OnError(*r);
581  }
582  else
583  li->OnResult(*r);
584  }
585 
586  delete r;
587  }
588  }
589  }
590 };
591 
593 
void OnNotify() anope_override
Definition: m_ldap.cpp:559
int CountBlock(const Anope::string &name)
Definition: config.cpp:35
std::vector< Anope::string > values
Definition: ldap.h:25
LDAPQuery Bind(LDAPInterface *i, const Anope::string &who, const Anope::string &pass) anope_override
Definition: m_ldap.cpp:135
bool GetExitState() const
int port
Definition: m_ldap.cpp:13
void OnModuleUnload(User *, Module *m) anope_override
Definition: m_ldap.cpp:526
Definition: users.h:34
virtual void OnDelete()
Definition: ldap.h:119
virtual void OnResult(const LDAPResult &r)=0
LDAPQuery Del(LDAPInterface *i, const Anope::string &dn) anope_override
Definition: m_ldap.cpp:221
result_queue results
Definition: m_ldap.cpp:78
void Unlock()
LDAP * con
Definition: m_ldap.cpp:18
void Start()
CoreExport time_t CurTime
Definition: main.cpp:41
Definition: sockets.h:454
iterator erase(const iterator &i)
Definition: anope.h:155
void SetExitState()
LDAPQuery Search(LDAPInterface *i, const Anope::string &base, const Anope::string &filter) anope_override
Definition: m_ldap.cpp:166
std::vector< LDAPAttributes > messages
Definition: ldap.h:64
void Wakeup()
ModuleLDAP(const Anope::string &modname, const Anope::string &creator)
Definition: m_ldap.cpp:452
Block * GetBlock(const Anope::string &name, int num=0)
Definition: config.cpp:43
std::map< Anope::string, LDAPService * > LDAPServices
Definition: m_ldap.cpp:449
void Notify()
Definition: pipeengine.cpp:77
LDAPQuery Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) anope_override
Definition: m_ldap.cpp:248
int LDAPQuery
Definition: ldap.h:4
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
LDAPService(Module *o, const Anope::string &n, const Anope::string &s, int po, const Anope::string &b, const Anope::string &p, time_t t)
Definition: m_ldap.cpp:80
LDAPQuery id
Definition: ldap.h:78
std::vector< LDAPModification > LDAPMods
Definition: ldap.h:27
#define MODULE_INIT(x)
Definition: modules.h:45
static Timer * timeout
Definition: os_defcon.cpp:106
void Lock()
Anope::string admin_pass
Definition: m_ldap.cpp:15
LDAPQuery BindAsAdmin(LDAPInterface *i)
Definition: m_ldap.cpp:130
void Reconnect()
Definition: m_ldap.cpp:61
QueryType type
Definition: ldap.h:77
Module * owner
Definition: ldap.h:112
Anope::string error
Definition: ldap.h:65
Anope::string name
Definition: service.h:88
Anope::string name
Definition: ldap.h:24
Anope::string admin_binddn
Definition: m_ldap.cpp:14
std::vector< std::pair< LDAPInterface *, LDAPResult * > > result_queue
Definition: m_ldap.cpp:76
Anope::string server
Definition: m_ldap.cpp:12
~LDAPService()
Definition: m_ldap.cpp:97
~ModuleLDAP()
Definition: m_ldap.cpp:458
virtual const Anope::string & GetReason() const
Definition: anope.h:672
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
T Get(const Anope::string &tag)
Definition: config.h:44
std::map< LDAPQuery, std::pair< time_t, LDAPInterface * > > query_queue
Definition: m_ldap.cpp:75
LDAPMod ** BuildMods(const LDAPMods &attributes)
Definition: m_ldap.cpp:22
virtual void OnError(const LDAPResult &err)=0
void Timeout()
Definition: m_ldap.cpp:278
const Anope::string & getError() const
Definition: ldap.h:103
static Pipe * me
Definition: m_ldap.cpp:8
void FreeMods(LDAPMod **mods)
Definition: m_ldap.cpp:49
LDAPQuery Add(LDAPInterface *i, const Anope::string &dn, LDAPMods &attributes) anope_override
Definition: m_ldap.cpp:192
void Run() anope_override
Definition: m_ldap.cpp:304
LDAPOperation op
Definition: ldap.h:23
time_t timeout
Definition: m_ldap.cpp:16
void OnReload(Configuration::Conf *config) anope_override
Definition: m_ldap.cpp:470
query_queue queries
Definition: m_ldap.cpp:77
Definition: modules.h:163
time_t last_connect
Definition: m_ldap.cpp:20