anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
m_ldap.cpp (14263B)
1 /* 2 * 3 * (C) 2011-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 /* RequiredLibraries: ldap_r|ldap,lber */ 13 /* RequiredWindowsLibraries: libldap_r|libldap,liblber */ 14 15 #include "module.h" 16 #include "modules/ldap.h" 17 #include <ldap.h> 18 19 #if defined LDAP_API_FEATURE_X_OPENLDAP_REENTRANT && !LDAP_API_FEATURE_X_OPENLDAP_REENTRANT 20 # error Anope requires OpenLDAP to be built as reentrant. 21 #endif 22 23 24 class LDAPService; 25 static Pipe *me; 26 27 class LDAPRequest 28 { 29 public: 30 LDAPService *service; 31 LDAPInterface *inter; 32 LDAPMessage *message; /* message returned by ldap_ */ 33 LDAPResult *result; /* final result */ 34 struct timeval tv; 35 QueryType type; 36 37 LDAPRequest(LDAPService *s, LDAPInterface *i) 38 : service(s) 39 , inter(i) 40 , message(NULL) 41 , result(NULL) 42 { 43 type = QUERY_UNKNOWN; 44 tv.tv_sec = 0; 45 tv.tv_usec = 100000; 46 } 47 48 virtual ~LDAPRequest() 49 { 50 delete result; 51 if (inter != NULL) 52 inter->OnDelete(); 53 if (message != NULL) 54 ldap_msgfree(message); 55 } 56 57 virtual int run() = 0; 58 }; 59 60 class LDAPBind : public LDAPRequest 61 { 62 Anope::string who, pass; 63 64 public: 65 LDAPBind(LDAPService *s, LDAPInterface *i, const Anope::string &w, const Anope::string &p) 66 : LDAPRequest(s, i) 67 , who(w) 68 , pass(p) 69 { 70 type = QUERY_BIND; 71 } 72 73 int run() anope_override; 74 }; 75 76 class LDAPSearch : public LDAPRequest 77 { 78 Anope::string base; 79 Anope::string filter; 80 81 public: 82 LDAPSearch(LDAPService *s, LDAPInterface *i, const Anope::string &b, const Anope::string &f) 83 : LDAPRequest(s, i) 84 , base(b) 85 , filter(f) 86 { 87 type = QUERY_SEARCH; 88 } 89 90 int run() anope_override; 91 }; 92 93 class LDAPAdd : public LDAPRequest 94 { 95 Anope::string dn; 96 LDAPMods attributes; 97 98 public: 99 LDAPAdd(LDAPService *s, LDAPInterface *i, const Anope::string &d, const LDAPMods &attr) 100 : LDAPRequest(s, i) 101 , dn(d) 102 , attributes(attr) 103 { 104 type = QUERY_ADD; 105 } 106 107 int run() anope_override; 108 }; 109 110 class LDAPDel : public LDAPRequest 111 { 112 Anope::string dn; 113 114 public: 115 LDAPDel(LDAPService *s, LDAPInterface *i, const Anope::string &d) 116 : LDAPRequest(s, i) 117 , dn(d) 118 { 119 type = QUERY_DELETE; 120 } 121 122 int run() anope_override; 123 }; 124 125 class LDAPModify : public LDAPRequest 126 { 127 Anope::string base; 128 LDAPMods attributes; 129 130 public: 131 LDAPModify(LDAPService *s, LDAPInterface *i, const Anope::string &b, const LDAPMods &attr) 132 : LDAPRequest(s, i) 133 , base(b) 134 , attributes(attr) 135 { 136 type = QUERY_MODIFY; 137 } 138 139 int run() anope_override; 140 }; 141 142 class LDAPService : public LDAPProvider, public Thread, public Condition 143 { 144 Anope::string server; 145 Anope::string admin_binddn; 146 Anope::string admin_pass; 147 148 LDAP *con; 149 150 time_t last_connect; 151 152 public: 153 static LDAPMod **BuildMods(const LDAPMods &attributes) 154 { 155 LDAPMod **mods = new LDAPMod*[attributes.size() + 1]; 156 memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1)); 157 for (unsigned x = 0; x < attributes.size(); ++x) 158 { 159 const LDAPModification &l = attributes[x]; 160 mods[x] = new LDAPMod(); 161 162 if (l.op == LDAPModification::LDAP_ADD) 163 mods[x]->mod_op = LDAP_MOD_ADD; 164 else if (l.op == LDAPModification::LDAP_DEL) 165 mods[x]->mod_op = LDAP_MOD_DELETE; 166 else if (l.op == LDAPModification::LDAP_REPLACE) 167 mods[x]->mod_op = LDAP_MOD_REPLACE; 168 else if (l.op != 0) 169 throw LDAPException("Unknown LDAP operation"); 170 mods[x]->mod_type = strdup(l.name.c_str()); 171 mods[x]->mod_values = new char*[l.values.size() + 1]; 172 memset(mods[x]->mod_values, 0, sizeof(char *) * (l.values.size() + 1)); 173 for (unsigned j = 0, c = 0; j < l.values.size(); ++j) 174 if (!l.values[j].empty()) 175 mods[x]->mod_values[c++] = strdup(l.values[j].c_str()); 176 } 177 return mods; 178 } 179 180 static void FreeMods(LDAPMod **mods) 181 { 182 for (int i = 0; mods[i] != NULL; ++i) 183 { 184 free(mods[i]->mod_type); 185 for (int j = 0; mods[i]->mod_values[j] != NULL; ++j) 186 free(mods[i]->mod_values[j]); 187 delete [] mods[i]->mod_values; 188 } 189 delete [] mods; 190 } 191 192 private: 193 void Connect() 194 { 195 int i = ldap_initialize(&this->con, this->server.c_str()); 196 if (i != LDAP_SUCCESS) 197 throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i)); 198 199 const int version = LDAP_VERSION3; 200 i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version); 201 if (i != LDAP_OPT_SUCCESS) 202 throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i)); 203 204 const struct timeval tv = { 0, 0 }; 205 i = ldap_set_option(this->con, LDAP_OPT_NETWORK_TIMEOUT, &tv); 206 if (i != LDAP_OPT_SUCCESS) 207 throw LDAPException("Unable to set timeout for " + this->name + ": " + ldap_err2string(i)); 208 } 209 210 void Reconnect() 211 { 212 /* Only try one connect a minute. It is an expensive blocking operation */ 213 if (last_connect > Anope::CurTime - 60) 214 throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast"); 215 last_connect = Anope::CurTime; 216 217 ldap_unbind_ext(this->con, NULL, NULL); 218 219 Connect(); 220 } 221 222 void QueueRequest(LDAPRequest *r) 223 { 224 this->Lock(); 225 this->queries.push_back(r); 226 this->Wakeup(); 227 this->Unlock(); 228 } 229 230 public: 231 typedef std::vector<LDAPRequest *> query_queue; 232 query_queue queries, results; 233 Mutex process_mutex; /* held when processing requests not in either queue */ 234 235 LDAPService(Module *o, const Anope::string &n, const Anope::string &s, const Anope::string &b, const Anope::string &p) : LDAPProvider(o, n), server(s), admin_binddn(b), admin_pass(p), last_connect(0) 236 { 237 Connect(); 238 } 239 240 ~LDAPService() 241 { 242 /* At this point the thread has stopped so we don't need to hold process_mutex */ 243 244 this->Lock(); 245 246 for (unsigned int i = 0; i < this->queries.size(); ++i) 247 { 248 LDAPRequest *req = this->queries[i]; 249 250 /* queries have no results yet */ 251 req->result = new LDAPResult(); 252 req->result->type = req->type; 253 req->result->error = "LDAP Interface is going away"; 254 if (req->inter) 255 req->inter->OnError(*req->result); 256 257 delete req; 258 } 259 this->queries.clear(); 260 261 for (unsigned int i = 0; i < this->results.size(); ++i) 262 { 263 LDAPRequest *req = this->results[i]; 264 265 /* even though this may have already finished successfully we return that it didn't */ 266 req->result->error = "LDAP Interface is going away"; 267 if (req->inter) 268 req->inter->OnError(*req->result); 269 270 delete req; 271 } 272 273 this->Unlock(); 274 275 ldap_unbind_ext(this->con, NULL, NULL); 276 } 277 278 void BindAsAdmin(LDAPInterface *i) anope_override 279 { 280 this->Bind(i, this->admin_binddn, this->admin_pass); 281 } 282 283 void Bind(LDAPInterface *i, const Anope::string &who, const Anope::string &pass) anope_override 284 { 285 LDAPBind *b = new LDAPBind(this, i, who, pass); 286 QueueRequest(b); 287 } 288 289 void Search(LDAPInterface *i, const Anope::string &base, const Anope::string &filter) anope_override 290 { 291 if (i == NULL) 292 throw LDAPException("No interface"); 293 294 LDAPSearch *s = new LDAPSearch(this, i, base, filter); 295 QueueRequest(s); 296 } 297 298 void Add(LDAPInterface *i, const Anope::string &dn, LDAPMods &attributes) anope_override 299 { 300 LDAPAdd *add = new LDAPAdd(this, i, dn, attributes); 301 QueueRequest(add); 302 } 303 304 void Del(LDAPInterface *i, const Anope::string &dn) anope_override 305 { 306 LDAPDel *del = new LDAPDel(this, i, dn); 307 QueueRequest(del); 308 } 309 310 void Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) anope_override 311 { 312 LDAPModify *mod = new LDAPModify(this, i, base, attributes); 313 QueueRequest(mod); 314 } 315 316 private: 317 void BuildReply(int res, LDAPRequest *req) 318 { 319 LDAPResult *ldap_result = req->result = new LDAPResult(); 320 req->result->type = req->type; 321 322 if (res != LDAP_SUCCESS) 323 { 324 ldap_result->error = ldap_err2string(res); 325 return; 326 } 327 328 if (req->message == NULL) 329 { 330 return; 331 } 332 333 /* a search result */ 334 335 for (LDAPMessage *cur = ldap_first_message(this->con, req->message); cur; cur = ldap_next_message(this->con, cur)) 336 { 337 LDAPAttributes attributes; 338 339 char *dn = ldap_get_dn(this->con, cur); 340 if (dn != NULL) 341 { 342 attributes["dn"].push_back(dn); 343 ldap_memfree(dn); 344 dn = NULL; 345 } 346 347 BerElement *ber = NULL; 348 349 for (char *attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber)) 350 { 351 berval **vals = ldap_get_values_len(this->con, cur, attr); 352 int count = ldap_count_values_len(vals); 353 354 std::vector<Anope::string> attrs; 355 for (int j = 0; j < count; ++j) 356 attrs.push_back(vals[j]->bv_val); 357 attributes[attr] = attrs; 358 359 ldap_value_free_len(vals); 360 ldap_memfree(attr); 361 } 362 363 if (ber != NULL) 364 ber_free(ber, 0); 365 366 ldap_result->messages.push_back(attributes); 367 } 368 } 369 370 void SendRequests() 371 { 372 process_mutex.Lock(); 373 374 query_queue q; 375 this->Lock(); 376 queries.swap(q); 377 this->Unlock(); 378 379 if (q.empty()) 380 { 381 process_mutex.Unlock(); 382 return; 383 } 384 385 for (unsigned int i = 0; i < q.size(); ++i) 386 { 387 LDAPRequest *req = q[i]; 388 int ret = req->run(); 389 390 if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT) 391 { 392 /* try again */ 393 try 394 { 395 Reconnect(); 396 } 397 catch (const LDAPException &) 398 { 399 } 400 401 ret = req->run(); 402 } 403 404 BuildReply(ret, req); 405 406 this->Lock(); 407 results.push_back(req); 408 this->Unlock(); 409 } 410 411 me->Notify(); 412 413 process_mutex.Unlock(); 414 } 415 416 public: 417 void Run() anope_override 418 { 419 while (!this->GetExitState()) 420 { 421 this->Lock(); 422 /* Queries can be non empty if one is pushed during SendRequests() */ 423 if (queries.empty()) 424 this->Wait(); 425 this->Unlock(); 426 427 SendRequests(); 428 } 429 } 430 431 LDAP* GetConnection() 432 { 433 return con; 434 } 435 }; 436 437 class ModuleLDAP : public Module, public Pipe 438 { 439 std::map<Anope::string, LDAPService *> LDAPServices; 440 441 public: 442 443 ModuleLDAP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR) 444 { 445 me = this; 446 } 447 448 ~ModuleLDAP() 449 { 450 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it) 451 { 452 it->second->SetExitState(); 453 it->second->Wakeup(); 454 it->second->Join(); 455 delete it->second; 456 } 457 LDAPServices.clear(); 458 } 459 460 void OnReload(Configuration::Conf *config) anope_override 461 { 462 Configuration::Block *conf = config->GetModule(this); 463 464 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end();) 465 { 466 const Anope::string &cname = it->first; 467 LDAPService *s = it->second; 468 int i; 469 470 ++it; 471 472 for (i = 0; i < conf->CountBlock("ldap"); ++i) 473 if (conf->GetBlock("ldap", i)->Get<const Anope::string>("name", "ldap/main") == cname) 474 break; 475 476 if (i == conf->CountBlock("ldap")) 477 { 478 Log(LOG_NORMAL, "ldap") << "LDAP: Removing server connection " << cname; 479 480 s->SetExitState(); 481 s->Wakeup(); 482 s->Join(); 483 delete s; 484 this->LDAPServices.erase(cname); 485 } 486 } 487 488 for (int i = 0; i < conf->CountBlock("ldap"); ++i) 489 { 490 Configuration::Block *ldap = conf->GetBlock("ldap", i); 491 492 const Anope::string &connname = ldap->Get<const Anope::string>("name", "ldap/main"); 493 494 if (this->LDAPServices.find(connname) == this->LDAPServices.end()) 495 { 496 const Anope::string &server = ldap->Get<const Anope::string>("server", "127.0.0.1"); 497 const Anope::string &admin_binddn = ldap->Get<const Anope::string>("admin_binddn"); 498 const Anope::string &admin_password = ldap->Get<const Anope::string>("admin_password"); 499 500 try 501 { 502 LDAPService *ss = new LDAPService(this, connname, server, admin_binddn, admin_password); 503 ss->Start(); 504 this->LDAPServices.insert(std::make_pair(connname, ss)); 505 506 Log(LOG_NORMAL, "ldap") << "LDAP: Successfully initialized server " << connname << " (" << server << ")"; 507 } 508 catch (const LDAPException &ex) 509 { 510 Log(LOG_NORMAL, "ldap") << "LDAP: " << ex.GetReason(); 511 } 512 } 513 } 514 } 515 516 void OnModuleUnload(User *, Module *m) anope_override 517 { 518 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it) 519 { 520 LDAPService *s = it->second; 521 522 s->process_mutex.Lock(); 523 s->Lock(); 524 525 for (unsigned int i = s->queries.size(); i > 0; --i) 526 { 527 LDAPRequest *req = s->queries[i - 1]; 528 LDAPInterface *li = req->inter; 529 530 if (li && li->owner == m) 531 { 532 s->queries.erase(s->queries.begin() + i - 1); 533 delete req; 534 } 535 } 536 for (unsigned int i = s->results.size(); i > 0; --i) 537 { 538 LDAPRequest *req = s->results[i - 1]; 539 LDAPInterface *li = req->inter; 540 541 if (li && li->owner == m) 542 { 543 s->results.erase(s->results.begin() + i - 1); 544 delete req; 545 } 546 } 547 548 s->Unlock(); 549 s->process_mutex.Unlock(); 550 } 551 } 552 553 void OnNotify() anope_override 554 { 555 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it) 556 { 557 LDAPService *s = it->second; 558 559 LDAPService::query_queue results; 560 s->Lock(); 561 results.swap(s->results); 562 s->Unlock(); 563 564 for (unsigned int i = 0; i < results.size(); ++i) 565 { 566 LDAPRequest *req = results[i]; 567 LDAPInterface *li = req->inter; 568 LDAPResult *r = req->result; 569 570 if (li != NULL) 571 { 572 if (!r->getError().empty()) 573 { 574 Log(this) << "Error running LDAP query: " << r->getError(); 575 li->OnError(*r); 576 } 577 else 578 li->OnResult(*r); 579 } 580 581 delete req; 582 } 583 } 584 } 585 }; 586 587 int LDAPBind::run() 588 { 589 berval cred; 590 cred.bv_val = strdup(pass.c_str()); 591 cred.bv_len = pass.length(); 592 593 int i = ldap_sasl_bind_s(service->GetConnection(), who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL); 594 595 free(cred.bv_val); 596 597 return i; 598 } 599 600 int LDAPSearch::run() 601 { 602 return ldap_search_ext_s(service->GetConnection(), base.c_str(), LDAP_SCOPE_SUBTREE, filter.c_str(), NULL, 0, NULL, NULL, &tv, 0, &message); 603 } 604 605 int LDAPAdd::run() 606 { 607 LDAPMod **mods = LDAPService::BuildMods(attributes); 608 int i = ldap_add_ext_s(service->GetConnection(), dn.c_str(), mods, NULL, NULL); 609 LDAPService::FreeMods(mods); 610 return i; 611 } 612 613 int LDAPDel::run() 614 { 615 return ldap_delete_ext_s(service->GetConnection(), dn.c_str(), NULL, NULL); 616 } 617 618 int LDAPModify::run() 619 { 620 LDAPMod **mods = LDAPService::BuildMods(attributes); 621 int i = ldap_modify_ext_s(service->GetConnection(), base.c_str(), mods, NULL, NULL); 622 LDAPService::FreeMods(mods); 623 return i; 624 } 625 626 MODULE_INIT(ModuleLDAP)