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)