m_ldap.cpp

Go to the documentation of this file.
00001 /* RequiredLibraries: ldap,lber */
00002 
00003 #include "module.h"
00004 #include "ldap.h"
00005 #include <ldap.h>
00006 
00007 static Pipe *me;
00008 
00009 class LDAPService : public LDAPProvider, public Thread, public Condition
00010 {
00011         Anope::string server;
00012         int port;
00013         Anope::string admin_binddn;
00014         Anope::string admin_pass;
00015 
00016         LDAP *con;
00017 
00018         time_t last_connect;
00019 
00020         LDAPMod **BuildMods(const LDAPMods &attributes)
00021         {
00022                 LDAPMod **mods = new LDAPMod*[attributes.size() + 1];
00023                 memset(mods, 0, sizeof(LDAPMod*) * (attributes.size() + 1));
00024                 for (unsigned x = 0; x < attributes.size(); ++x)
00025                 {
00026                         const LDAPModification &l = attributes[x];
00027                         mods[x] = new LDAPMod();
00028 
00029                         if (l.op == LDAPModification::LDAP_ADD)
00030                                 mods[x]->mod_op = LDAP_MOD_ADD;
00031                         else if (l.op == LDAPModification::LDAP_DEL)
00032                                 mods[x]->mod_op = LDAP_MOD_DELETE;
00033                         else if (l.op == LDAPModification::LDAP_REPLACE)
00034                                 mods[x]->mod_op = LDAP_MOD_REPLACE;
00035                         else if (l.op != 0)
00036                                 throw LDAPException("Unknown LDAP operation");
00037                         mods[x]->mod_type = strdup(l.name.c_str());
00038                         mods[x]->mod_values = new char*[l.values.size() + 1];
00039                         memset(mods[x]->mod_values, 0, sizeof(char *) * (l.values.size() + 1));
00040                         for (unsigned j = 0, c = 0; j < l.values.size(); ++j)
00041                                 if (!l.values[j].empty())
00042                                         mods[x]->mod_values[c++] = strdup(l.values[j].c_str());
00043                 }
00044                 return mods;
00045         }
00046 
00047         void FreeMods(LDAPMod **mods)
00048         {
00049                 for (int i = 0; mods[i] != NULL; ++i)
00050                 {
00051                         free(mods[i]->mod_type);
00052                         for (int j = 0; mods[i]->mod_values[j] != NULL; ++j)
00053                                 free(mods[i]->mod_values[j]);
00054                         delete [] mods[i]->mod_values;
00055                 }
00056                 delete [] mods;
00057         }
00058 
00059         void Reconnect()
00060         {
00061                 /* Only try one connect a minute. It is an expensive blocking operation */
00062                 if (last_connect > Anope::CurTime - 60)
00063                         throw LDAPException("Unable to connect to LDAP service " + this->name + ": reconnecting too fast");
00064                 last_connect = Anope::CurTime;
00065 
00066                 ldap_unbind_ext(this->con, NULL, NULL);
00067                 int i = ldap_initialize(&this->con, this->server.c_str());
00068                 if (i != LDAP_SUCCESS)
00069                         throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
00070         }
00071 
00072  public:
00073         typedef std::map<int, LDAPInterface *> query_queue;
00074         typedef std::vector<std::pair<LDAPInterface *, LDAPResult *> > result_queue;
00075         query_queue queries;
00076         result_queue results;
00077 
00078         LDAPService(Module *o, const Anope::string &n, const Anope::string &s, int po, const Anope::string &b, const Anope::string &p) : LDAPProvider(o, "ldap/" + n), server(s), port(po), admin_binddn(b), admin_pass(p), last_connect(0)
00079         {
00080                 int i = ldap_initialize(&this->con, this->server.c_str());
00081                 if (i != LDAP_SUCCESS)
00082                         throw LDAPException("Unable to connect to LDAP service " + this->name + ": " + ldap_err2string(i));
00083                 static const int version = LDAP_VERSION3;
00084                 i = ldap_set_option(this->con, LDAP_OPT_PROTOCOL_VERSION, &version);
00085                 if (i != LDAP_OPT_SUCCESS)
00086                         throw LDAPException("Unable to set protocol version for " + this->name + ": " + ldap_err2string(i));
00087         }
00088 
00089         ~LDAPService()
00090         {
00091                 this->Lock();
00092 
00093                 for (query_queue::iterator it = this->queries.begin(), it_end = this->queries.end(); it != it_end; ++it)
00094                 {
00095                         ldap_abandon_ext(this->con, it->first, NULL, NULL);
00096                         it->second->OnDelete();
00097                 }
00098                 this->queries.clear();
00099 
00100                 for (result_queue::iterator it = this->results.begin(), it_end = this->results.end(); it != it_end; ++it)
00101                 {
00102                         it->second->error = "LDAP Interface is going away";
00103                         it->first->OnError(*it->second);
00104                 }
00105                 this->results.clear();
00106 
00107                 this->Unlock();
00108 
00109                 ldap_unbind_ext(this->con, NULL, NULL);
00110         }
00111         
00112         LDAPQuery BindAsAdmin(LDAPInterface *i)
00113         {
00114                 return this->Bind(i, this->admin_binddn, this->admin_pass);
00115         }
00116 
00117         LDAPQuery Bind(LDAPInterface *i, const Anope::string &who, const Anope::string &pass) anope_override
00118         {
00119                 berval cred;
00120                 cred.bv_val = strdup(pass.c_str());
00121                 cred.bv_len = pass.length();
00122 
00123                 LDAPQuery msgid;
00124                 int ret = ldap_sasl_bind(con, who.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, &msgid);
00125                 free(cred.bv_val);
00126                 if (ret != LDAP_SUCCESS)
00127                 {
00128                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
00129                         {
00130                                 this->Reconnect();
00131                                 return this->Bind(i, who, pass);
00132                         }
00133                         else
00134                                 throw LDAPException(ldap_err2string(ret));
00135                 }
00136 
00137                 if (i != NULL)
00138                 {
00139                         this->Lock();
00140                         this->queries[msgid] = i;
00141                         this->Unlock();
00142                 }
00143                 this->Wakeup();
00144 
00145                 return msgid;
00146         }
00147 
00148         LDAPQuery Search(LDAPInterface *i, const Anope::string &base, const Anope::string &filter) anope_override
00149         {
00150                 if (i == NULL)
00151                         throw LDAPException("No interface");
00152 
00153                 LDAPQuery msgid;
00154                 int ret = ldap_search_ext(this->con, base.c_str(), LDAP_SCOPE_SUBTREE, filter.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msgid);
00155                 if (ret != LDAP_SUCCESS)
00156                 {
00157                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
00158                         {
00159                                 this->Reconnect();
00160                                 return this->Search(i, base, filter);
00161                         }
00162                         else
00163                                 throw LDAPException(ldap_err2string(ret));
00164                 }
00165 
00166                 this->Lock();
00167                 this->queries[msgid] = i;
00168                 this->Unlock();
00169                 this->Wakeup();
00170 
00171                 return msgid;
00172         }
00173 
00174         LDAPQuery Add(LDAPInterface *i, const Anope::string &dn, LDAPMods &attributes) anope_override
00175         {
00176                 LDAPMod **mods = this->BuildMods(attributes);
00177                 LDAPQuery msgid;
00178                 int ret = ldap_add_ext(this->con, dn.c_str(), mods, NULL, NULL, &msgid);
00179                 this->FreeMods(mods);
00180 
00181                 if (ret != LDAP_SUCCESS)
00182                 {
00183                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
00184                         {
00185                                 this->Reconnect();
00186                                 return this->Add(i, dn, attributes);
00187                         }
00188                         else
00189                                 throw LDAPException(ldap_err2string(ret));
00190                 }
00191 
00192                 if (i != NULL)
00193                 {
00194                         this->Lock();
00195                         this->queries[msgid] = i;
00196                         this->Unlock();
00197                 }
00198                 this->Wakeup();
00199 
00200                 return msgid;
00201         }
00202 
00203         LDAPQuery Del(LDAPInterface *i, const Anope::string &dn) anope_override
00204         {
00205                 LDAPQuery msgid;
00206                 int ret = ldap_delete_ext(this->con, dn.c_str(), NULL, NULL, &msgid);
00207 
00208                 if (ret != LDAP_SUCCESS)
00209                 {
00210                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
00211                         {
00212                                 this->Reconnect();
00213                                 return this->Del(i, dn);
00214                         }
00215                         else
00216                                 throw LDAPException(ldap_err2string(ret));
00217                 }
00218 
00219                 if (i != NULL)
00220                 {
00221                         this->Lock();
00222                         this->queries[msgid] = i;
00223                         this->Unlock();
00224                 }
00225                 this->Wakeup();
00226 
00227                 return msgid;
00228         }
00229 
00230         LDAPQuery Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) anope_override
00231         {
00232                 LDAPMod **mods = this->BuildMods(attributes);
00233                 LDAPQuery msgid;
00234                 int ret = ldap_modify_ext(this->con, base.c_str(), mods, NULL, NULL, &msgid);
00235                 this->FreeMods(mods);
00236 
00237                 if (ret != LDAP_SUCCESS)
00238                 {
00239                         if (ret == LDAP_SERVER_DOWN || ret == LDAP_TIMEOUT)
00240                         {
00241                                 this->Reconnect();
00242                                 return this->Modify(i, base, attributes);
00243                         }
00244                         else
00245                                 throw LDAPException(ldap_err2string(ret));
00246                 }
00247 
00248                 if (i != NULL)
00249                 {
00250                         this->Lock();
00251                         this->queries[msgid] = i;
00252                         this->Unlock();
00253                 }
00254                 this->Wakeup();
00255 
00256                 return msgid;
00257         }
00258 
00259         void Run() anope_override
00260         {
00261                 while (!this->GetExitState())
00262                 {
00263                         if (this->queries.empty())
00264                         {
00265                                 this->Lock();
00266                                 this->Wait();
00267                                 this->Unlock();
00268                                 if (this->GetExitState())
00269                                         break;
00270                         }
00271 
00272                         static struct timeval tv = { 1, 0 };
00273                         LDAPMessage *result;
00274                         int rtype = ldap_result(this->con, LDAP_RES_ANY, 1, &tv, &result);
00275                         if (rtype <= 0 || this->GetExitState())
00276                                 continue;
00277 
00278                         int cur_id = ldap_msgid(result);
00279 
00280                         this->Lock();
00281 
00282                         query_queue::iterator it = this->queries.find(cur_id);
00283                         if (it == this->queries.end())
00284                         {
00285                                 this->Unlock();
00286                                 ldap_msgfree(result);
00287                                 continue;
00288                         }
00289                         LDAPInterface *i = it->second;
00290                         this->queries.erase(it);
00291 
00292                         this->Unlock();
00293 
00294                         LDAPResult *ldap_result = new LDAPResult();
00295                         ldap_result->id = cur_id;
00296 
00297                         for (LDAPMessage *cur = ldap_first_message(this->con, result); cur; cur = ldap_next_message(this->con, cur))
00298                         {
00299                                 int cur_type = ldap_msgtype(cur);
00300 
00301                                 LDAPAttributes attributes;
00302 
00303                                 char *dn = ldap_get_dn(this->con, cur);
00304                                 if (dn != NULL)
00305                                 {
00306                                         attributes["dn"].push_back(dn);
00307                                         ldap_memfree(dn);
00308                                         dn = NULL;
00309                                 }
00310 
00311                                 switch (cur_type)
00312                                 {
00313                                         case LDAP_RES_BIND:
00314                                                 ldap_result->type = LDAPResult::QUERY_BIND;
00315                                                 break;
00316                                         case LDAP_RES_SEARCH_ENTRY:
00317                                                 ldap_result->type = LDAPResult::QUERY_SEARCH;
00318                                                 break;
00319                                         case LDAP_RES_ADD:
00320                                                 ldap_result->type = LDAPResult::QUERY_ADD;
00321                                                 break;
00322                                         case LDAP_RES_DELETE:
00323                                                 ldap_result->type = LDAPResult::QUERY_DELETE;
00324                                                 break;
00325                                         case LDAP_RES_MODIFY:
00326                                                 ldap_result->type = LDAPResult::QUERY_MODIFY;
00327                                                 break;
00328                                         case LDAP_RES_SEARCH_RESULT:
00329                                                 // If we get here and ldap_result->type is LDAPResult::QUERY_UNKNOWN
00330                                                 // then the result set is empty
00331                                                 ldap_result->type = LDAPResult::QUERY_SEARCH;
00332                                                 break;
00333                                         default:
00334                                                 Log(LOG_DEBUG) << "m_ldap: Unknown msg type " << cur_type;
00335                                                 continue;
00336                                 }
00337 
00338                                 switch (cur_type)
00339                                 {
00340                                         case LDAP_RES_BIND:
00341                                         {
00342                                                 int errcode = -1;
00343                                                 int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
00344                                                 if (parse_result != LDAP_SUCCESS)
00345                                                         ldap_result->error = ldap_err2string(parse_result);
00346                                                 else if (errcode != LDAP_SUCCESS)
00347                                                         ldap_result->error = ldap_err2string(errcode);
00348                                                 break;
00349                                         }
00350                                         case LDAP_RES_SEARCH_ENTRY:
00351                                         {
00352                                                 BerElement *ber = NULL;
00353                                                 for (char *attr = ldap_first_attribute(this->con, cur, &ber); attr; attr = ldap_next_attribute(this->con, cur, ber))
00354                                                 {
00355                                                         berval **vals = ldap_get_values_len(this->con, cur, attr);
00356                                                         int count = ldap_count_values_len(vals);
00357 
00358                                                         std::vector<Anope::string> attrs;
00359                                                         for (int j = 0; j < count; ++j)
00360                                                                 attrs.push_back(vals[j]->bv_val);
00361                                                         attributes[attr] = attrs;
00362 
00363                                                         ldap_value_free_len(vals);
00364                                                         ldap_memfree(attr);
00365                                                 }
00366                                                 if (ber != NULL)
00367                                                         ber_free(ber, 0);
00368 
00369                                                 break;
00370                                         }
00371                                         case LDAP_RES_ADD:
00372                                         case LDAP_RES_DELETE:
00373                                         case LDAP_RES_MODIFY:
00374                                         {
00375                                                 int errcode = -1;
00376                                                 int parse_result = ldap_parse_result(this->con, cur, &errcode, NULL, NULL, NULL, NULL, 0);
00377                                                 if (parse_result != LDAP_SUCCESS)
00378                                                         ldap_result->error = ldap_err2string(parse_result);
00379                                                 else if (errcode != LDAP_SUCCESS)
00380                                                         ldap_result->error = ldap_err2string(errcode);
00381                                                 break;
00382                                         }
00383                                         default:
00384                                                 continue;
00385                                 }
00386 
00387                                 ldap_result->messages.push_back(attributes);
00388                         }
00389 
00390                         ldap_msgfree(result);
00391 
00392                         this->Lock();
00393                         this->results.push_back(std::make_pair(i, ldap_result));
00394                         this->Unlock();
00395 
00396                         me->Notify();
00397                 }
00398         }
00399 };
00400 
00401 class ModuleLDAP : public Module, public Pipe
00402 {
00403         std::map<Anope::string, LDAPService *> LDAPServices;
00404  public:
00405 
00406         ModuleLDAP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED)
00407         {
00408                 me = this;
00409 
00410                 Implementation i[] = { I_OnReload, I_OnModuleUnload };
00411                 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00412 
00413                 OnReload();
00414         }
00415 
00416         ~ModuleLDAP()
00417         {
00418                 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
00419                 {
00420                         it->second->SetExitState();
00421                         it->second->Wakeup();
00422                         it->second->Join();
00423                         delete it->second;
00424                 }
00425                 LDAPServices.clear();
00426         }
00427 
00428         void OnReload() anope_override
00429         {
00430                 ConfigReader config;
00431                 int i, num;
00432 
00433                 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end();)
00434                 {
00435                         const Anope::string &cname = it->first;
00436                         LDAPService *s = it->second;
00437                         ++it;
00438 
00439                         for (i = 0, num = config.Enumerate("ldap"); i < num; ++i)
00440                         {
00441                                 if (config.ReadValue("ldap", "name", "main", i) == cname)
00442                                 {
00443                                         break;
00444                                 }
00445                         }
00446 
00447                         if (i == num)
00448                         {
00449                                 Log(LOG_NORMAL, "ldap") << "LDAP: Removing server connection " << cname;
00450 
00451                                 s->SetExitState();
00452                                 s->Wakeup();
00453                                 this->LDAPServices.erase(cname);
00454                         }
00455                 }
00456 
00457                 for (i = 0, num = config.Enumerate("ldap"); i < num; ++i)
00458                 {
00459                         Anope::string connname = config.ReadValue("ldap", "name", "main", i);
00460 
00461                         if (this->LDAPServices.find(connname) == this->LDAPServices.end())
00462                         {
00463                                 Anope::string server = config.ReadValue("ldap", "server", "127.0.0.1", i);
00464                                 int port = config.ReadInteger("ldap", "port", "389", i, true);
00465                                 Anope::string admin_binddn = config.ReadValue("ldap", "admin_binddn", "", i);
00466                                 Anope::string admin_password = config.ReadValue("ldap", "admin_password", "", i);
00467 
00468                                 try
00469                                 {
00470                                         LDAPService *ss = new LDAPService(this, connname, server, port, admin_binddn, admin_password);
00471                                         ss->Start();
00472                                         this->LDAPServices.insert(std::make_pair(connname, ss));
00473 
00474                                         Log(LOG_NORMAL, "ldap") << "LDAP: Successfully connected to server " << connname << " (" << server << ")";
00475                                 }
00476                                 catch (const LDAPException &ex)
00477                                 {
00478                                         Log(LOG_NORMAL, "ldap") << "LDAP: " << ex.GetReason();
00479                                 }
00480                         }
00481                 }
00482         }
00483 
00484         void OnModuleUnload(User *, Module *m) anope_override
00485         {
00486                 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
00487                 {
00488                         LDAPService *s = it->second;
00489                         s->Lock();
00490                         for (LDAPService::query_queue::iterator it2 = s->queries.begin(); it2 != s->queries.end();)
00491                         {
00492                                 int msgid = it2->first;
00493                                 LDAPInterface *i = it2->second;
00494                                 ++it2;
00495 
00496                                 if (i->owner == m)
00497                                 {
00498                                         i->OnDelete();
00499                                         s->queries.erase(msgid);
00500                                 }
00501                         }
00502                         for (unsigned i = s->results.size(); i > 0; --i)
00503                         {
00504                                 LDAPInterface *li = s->results[i - 1].first;
00505                                 if (li->owner == m)
00506                                         s->results.erase(s->results.begin() + i - 1);
00507                         }
00508                         s->Unlock();
00509                 } 
00510         }
00511 
00512         void OnNotify() anope_override
00513         {
00514                 for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
00515                 {
00516                         LDAPService *s = it->second;
00517 
00518                         s->Lock();
00519                         LDAPService::result_queue results = s->results;
00520                         s->results.clear();
00521                         s->Unlock();
00522 
00523                         for (unsigned i = 0; i < results.size(); ++i)
00524                         {
00525                                 LDAPInterface *li = results[i].first;
00526                                 LDAPResult *r = results[i].second;
00527 
00528                                 if (!r->error.empty())
00529                                         li->OnError(*r);
00530                                 else
00531                                         li->OnResult(*r);
00532                         }
00533                 } 
00534         }
00535 };
00536 
00537 MODULE_INIT(ModuleLDAP)
00538