m_ldap_authentication.cpp

Go to the documentation of this file.
00001 #include "module.h"
00002 #include "nickserv.h"
00003 #include "ldap.h"
00004 
00005 static Module *me;
00006 
00007 static Anope::string basedn;
00008 static Anope::string search_filter;
00009 static Anope::string object_class;
00010 static Anope::string email_attribute;
00011 static Anope::string username_attribute;
00012 
00013 struct IdentifyInfo
00014 {
00015         Reference<User> user;
00016         IdentifyRequest *req;
00017         ServiceReference<LDAPProvider> lprov;
00018         bool admin_bind;
00019         Anope::string dn;
00020 
00021         IdentifyInfo(User *u, IdentifyRequest *r, ServiceReference<LDAPProvider> &lp) : user(u), req(r), lprov(lp), admin_bind(true)
00022         {
00023                 req->Hold(me);
00024         }
00025         
00026         ~IdentifyInfo()
00027         {
00028                 req->Release(me);
00029         }
00030 };
00031 
00032 class IdentifyInterface : public LDAPInterface
00033 {
00034         std::map<LDAPQuery, IdentifyInfo *> requests;
00035 
00036  public:
00037         IdentifyInterface(Module *m) : LDAPInterface(m) { }
00038 
00039         void Add(LDAPQuery id, IdentifyInfo *ii)
00040         {
00041                 std::map<LDAPQuery, IdentifyInfo *>::iterator it = this->requests.find(id);
00042                 if (it != this->requests.end())
00043                         delete it->second;
00044                 this->requests[id] = ii;
00045         }
00046 
00047         void OnResult(const LDAPResult &r) anope_override
00048         {
00049                 std::map<LDAPQuery, IdentifyInfo *>::iterator it = this->requests.find(r.id);
00050                 if (it == this->requests.end())
00051                         return;
00052                 IdentifyInfo *ii = it->second;
00053                 this->requests.erase(it);
00054 
00055                 if (!ii->lprov)
00056                 {
00057                         delete ii;
00058                         return;
00059                 }
00060 
00061                 switch (r.type)
00062                 {
00063                         case LDAPResult::QUERY_SEARCH:
00064                         {
00065                                 if (!r.empty())
00066                                 {
00067                                         try
00068                                         {
00069                                                 const LDAPAttributes &attr = r.get(0);
00070                                                 ii->dn = attr.get("dn");
00071                                                 Log(LOG_DEBUG) << "m_ldap_authenticationn: binding as " << ii->dn;
00072                                                 LDAPQuery id = ii->lprov->Bind(this, ii->dn, ii->req->GetPassword());
00073                                                 this->Add(id, ii);
00074                                                 return;
00075                                         }
00076                                         catch (const LDAPException &ex)
00077                                         {
00078                                                 Log(this->owner) << "m_ldap_authentication: Error binding after search: " << ex.GetReason();
00079                                         }
00080                                 }
00081                                 break;
00082                         }
00083                         case LDAPResult::QUERY_BIND:
00084                         {
00085                                 if (ii->admin_bind)
00086                                 {
00087                                         Anope::string sf = search_filter.replace_all_cs("%account", ii->req->GetAccount()).replace_all_cs("%object_class", object_class);
00088                                         try
00089                                         {
00090                                                 Log(LOG_DEBUG) << "m_ldap_authentication: searching for " << sf;
00091                                                 LDAPQuery id = ii->lprov->Search(this, basedn, sf);
00092                                                 this->Add(id, ii);
00093                                                 ii->admin_bind = false;
00094                                                 return;
00095                                         }
00096                                         catch (const LDAPException &ex)
00097                                         {
00098                                                 Log(this->owner) << "m_ldap_authentication: Unable to search for " << sf << ": " << ex.GetReason();
00099                                         }
00100                                 }
00101                                 else
00102                                 {
00103                                         NickAlias *na = NickAlias::Find(ii->req->GetAccount());
00104                                         if (na == NULL)
00105                                         {
00106                                                 na = new NickAlias(ii->req->GetAccount(), new NickCore(ii->req->GetAccount()));
00107                                                 if (ii->user)
00108                                                 {
00109                                                         if (Config->NSAddAccessOnReg)
00110                                                                 na->nc->AddAccess(ii->user->Mask());
00111 
00112                                                         if (NickServ)
00113                                                                 ii->user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->nick.c_str());
00114                                                 }
00115                                         }
00116                                         // encrypt and store the password in the nickcore
00117                                         Anope::Encrypt(ii->req->GetPassword(), na->nc->pass);
00118 
00119                                         na->nc->Extend("m_ldap_authentication_dn", new ExtensibleItemClass<Anope::string>(ii->dn));
00120                                         ii->req->Success(me);
00121                                 }
00122                                 break;
00123                         }
00124                         default:
00125                                 break;
00126                 }
00127 
00128                 delete ii;
00129         }
00130 
00131         void OnError(const LDAPResult &r) anope_override
00132         {
00133                 std::map<LDAPQuery, IdentifyInfo *>::iterator it = this->requests.find(r.id);
00134                 if (it == this->requests.end())
00135                         return;
00136                 IdentifyInfo *ii = it->second;
00137                 this->requests.erase(it);
00138                 delete ii;
00139         }
00140 };
00141 
00142 class OnIdentifyInterface : public LDAPInterface
00143 {
00144         std::map<LDAPQuery, Anope::string> requests;
00145 
00146  public:
00147         OnIdentifyInterface(Module *m) : LDAPInterface(m) { }
00148 
00149         void Add(LDAPQuery id, const Anope::string &nick)
00150         {
00151                 this->requests[id] = nick;
00152         }
00153 
00154         void OnResult(const LDAPResult &r) anope_override
00155         {
00156                 std::map<LDAPQuery, Anope::string>::iterator it = this->requests.find(r.id);
00157                 if (it == this->requests.end())
00158                         return;
00159                 User *u = User::Find(it->second);
00160                 this->requests.erase(it);
00161 
00162                 if (!u || !u->Account() || r.empty())
00163                         return;
00164 
00165                 try
00166                 {
00167                         const LDAPAttributes &attr = r.get(0);
00168                         Anope::string email = attr.get(email_attribute);
00169 
00170                         if (!email.equals_ci(u->Account()->email))
00171                         {
00172                                 u->Account()->email = email;
00173                                 if (NickServ)
00174                                         u->SendMessage(NickServ, _("Your email has been updated to \002%s\002"), email.c_str());
00175                                 Log(this->owner) << "m_ldap_authentication: Updated email address for " << u->nick << " (" << u->Account()->display << ") to " << email;
00176                         }
00177                 }
00178                 catch (const LDAPException &ex)
00179                 {
00180                         Log(this->owner) << "m_ldap_authentication: " << ex.GetReason();
00181                 }
00182         }
00183 
00184         void OnError(const LDAPResult &r) anope_override
00185         {
00186                 this->requests.erase(r.id);
00187                 Log(this->owner) << "m_ldap_authentication: " << r.error;
00188         }
00189 };
00190 
00191 class OnRegisterInterface : public LDAPInterface
00192 {
00193  public:
00194         OnRegisterInterface(Module *m) : LDAPInterface(m) { }
00195 
00196         void OnResult(const LDAPResult &r) anope_override
00197         {
00198                 Log(this->owner) << "m_ldap_authentication: Successfully added newly created account to LDAP";
00199         }
00200 
00201         void OnError(const LDAPResult &r) anope_override
00202         {
00203                 Log(this->owner) << "m_ldap_authentication: Error adding newly created account to LDAP: " << r.getError();
00204         }
00205 };
00206 
00207 class NSIdentifyLDAP : public Module
00208 {
00209         ServiceReference<LDAPProvider> ldap;
00210         IdentifyInterface iinterface;
00211         OnIdentifyInterface oninterface;
00212         OnRegisterInterface orinterface;
00213 
00214         Anope::string password_attribute;
00215         Anope::string disable_register_reason;
00216         Anope::string disable_email_reason;
00217  public:
00218         NSIdentifyLDAP(const Anope::string &modname, const Anope::string &creator) :
00219                 Module(modname, creator, SUPPORTED), ldap("LDAPProvider", "ldap/main"), iinterface(this), oninterface(this), orinterface(this)
00220         {
00221                 this->SetAuthor("Anope");
00222 
00223                 me = this;
00224 
00225                 Implementation i[] = { I_OnReload, I_OnPreCommand, I_OnCheckAuthentication, I_OnNickIdentify, I_OnNickRegister };
00226                 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00227                 ModuleManager::SetPriority(this, PRIORITY_FIRST);
00228 
00229                 OnReload();
00230         }
00231 
00232         void OnReload() anope_override
00233         {
00234                 ConfigReader config;
00235 
00236                 basedn = config.ReadValue("m_ldap_authentication", "basedn", "", 0);
00237                 search_filter = config.ReadValue("m_ldap_authentication", "search_filter", "", 0);
00238                 object_class = config.ReadValue("m_ldap_authentication", "object_class", "", 0);
00239                 username_attribute = config.ReadValue("m_ldap_authentication", "username_attribute", "", 0);
00240                 this->password_attribute = config.ReadValue("m_ldap_authentication", "password_attribute", "", 0);
00241                 email_attribute = config.ReadValue("m_ldap_authentication", "email_attribute", "", 0);
00242                 this->disable_register_reason = config.ReadValue("m_ldap_authentication", "disable_register_reason", "", 0);
00243                 this->disable_email_reason = config.ReadValue("m_ldap_authentication", "disable_email_reason", "", 0);
00244 
00245                 if (!email_attribute.empty())
00246                         /* Don't complain to users about how they need to update their email, we will do it for them */
00247                         Config->NSForceEmail = false;
00248         }
00249 
00250         EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
00251         {
00252                 if (!this->disable_register_reason.empty() && command->name == "nickserv/register")
00253                 {
00254                         source.Reply(this->disable_register_reason);
00255                         return EVENT_STOP;
00256                 }
00257                 else if (!email_attribute.empty() && !this->disable_email_reason.empty() && command->name == "nickserv/set/email")
00258                 {
00259                         source.Reply(this->disable_email_reason);
00260                         return EVENT_STOP;
00261                 }
00262 
00263                 return EVENT_CONTINUE;
00264         }
00265 
00266         void OnCheckAuthentication(User *u, IdentifyRequest *req) anope_override
00267         {
00268                 if (!this->ldap)
00269                         return;
00270 
00271                 IdentifyInfo *ii = new IdentifyInfo(u, req, this->ldap);
00272                 try
00273                 {
00274                         LDAPQuery id = this->ldap->BindAsAdmin(&this->iinterface);
00275                         this->iinterface.Add(id, ii);
00276                 }
00277                 catch (const LDAPException &ex)
00278                 {
00279                         delete ii;
00280                         Log(this) << ex.GetReason();
00281                 }
00282         }
00283 
00284         void OnNickIdentify(User *u) anope_override
00285         {
00286                 if (email_attribute.empty() || !this->ldap || !u->Account()->HasExt("m_ldap_authentication_dn"))
00287                         return;
00288 
00289                 Anope::string *dn = u->Account()->GetExt<ExtensibleItemClass<Anope::string> *>("m_ldap_authentication_dn");
00290                 if (!dn || dn->empty())
00291                         return;
00292 
00293                 try
00294                 {
00295                         LDAPQuery id = this->ldap->Search(&this->oninterface, *dn, "(" + email_attribute + "=*)");
00296                         this->oninterface.Add(id, u->nick);
00297                 }
00298                 catch (const LDAPException &ex)
00299                 {
00300                         Log(this) << ex.GetReason();
00301                 }
00302         }
00303 
00304         void OnNickRegister(NickAlias *na) anope_override
00305         {
00306                 if (!this->disable_register_reason.empty() || !this->ldap)
00307                         return;
00308 
00309                 try
00310                 {
00311                         this->ldap->BindAsAdmin(NULL);
00312 
00313                         LDAPMods attributes;
00314                         attributes.resize(4);
00315 
00316                         attributes[0].name = "objectClass";
00317                         attributes[0].values.push_back("top");
00318                         attributes[0].values.push_back(object_class);
00319 
00320                         attributes[1].name = username_attribute;
00321                         attributes[1].values.push_back(na->nick);
00322 
00323                         if (!na->nc->email.empty())
00324                         {
00325                                 attributes[2].name = email_attribute;
00326                                 attributes[2].values.push_back(na->nc->email);
00327                         }
00328 
00329                         attributes[3].name = this->password_attribute;
00330                         attributes[3].values.push_back(na->nc->pass);
00331 
00332                         Anope::string new_dn = username_attribute + "=" + na->nick + "," + basedn;
00333                         this->ldap->Add(&this->orinterface, new_dn, attributes);
00334                 }
00335                 catch (const LDAPException &ex)
00336                 {
00337                         Log(this) << ex.GetReason();
00338                 }
00339         }
00340 };
00341 
00342 MODULE_INIT(NSIdentifyLDAP)