anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

m_ldap_authentication.cpp (7889B)

      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 
      9 #include "module.h"
     10 #include "modules/ldap.h"
     11 
     12 static Module *me;
     13 
     14 static Anope::string basedn;
     15 static Anope::string search_filter;
     16 static Anope::string object_class;
     17 static Anope::string email_attribute;
     18 static Anope::string username_attribute;
     19 
     20 struct IdentifyInfo
     21 {
     22 	Reference<User> user;
     23 	IdentifyRequest *req;
     24 	ServiceReference<LDAPProvider> lprov;
     25 	bool admin_bind;
     26 	Anope::string dn;
     27 
     28 	IdentifyInfo(User *u, IdentifyRequest *r, ServiceReference<LDAPProvider> &lp) : user(u), req(r), lprov(lp), admin_bind(true)
     29 	{
     30 		req->Hold(me);
     31 	}
     32 
     33 	~IdentifyInfo()
     34 	{
     35 		req->Release(me);
     36 	}
     37 };
     38 
     39 class IdentifyInterface : public LDAPInterface
     40 {
     41 	IdentifyInfo *ii;
     42 
     43  public:
     44 	IdentifyInterface(Module *m, IdentifyInfo *i) : LDAPInterface(m), ii(i) { }
     45 
     46 	~IdentifyInterface()
     47 	{
     48 		delete ii;
     49 	}
     50 
     51 	void OnDelete() anope_override
     52 	{
     53 		delete this;
     54 	}
     55 
     56 	void OnResult(const LDAPResult &r) anope_override
     57 	{
     58 		if (!ii->lprov)
     59 			return;
     60 
     61 		switch (r.type)
     62 		{
     63 			case QUERY_SEARCH:
     64 			{
     65 				if (!r.empty())
     66 				{
     67 					try
     68 					{
     69 						const LDAPAttributes &attr = r.get(0);
     70 						ii->dn = attr.get("dn");
     71 						Log(LOG_DEBUG) << "m_ldap_authenticationn: binding as " << ii->dn;
     72 
     73 						ii->lprov->Bind(new IdentifyInterface(this->owner, ii), ii->dn, ii->req->GetPassword());
     74 						ii = NULL;
     75 					}
     76 					catch (const LDAPException &ex)
     77 					{
     78 						Log(this->owner) << "Error binding after search: " << ex.GetReason();
     79 					}
     80 				}
     81 				break;
     82 			}
     83 			case QUERY_BIND:
     84 			{
     85 				if (ii->admin_bind)
     86 				{
     87 					Anope::string sf = search_filter.replace_all_cs("%account", ii->req->GetAccount()).replace_all_cs("%object_class", object_class);
     88 					try
     89 					{
     90 						Log(LOG_DEBUG) << "m_ldap_authentication: searching for " << sf;
     91 						ii->lprov->Search(new IdentifyInterface(this->owner, ii), basedn, sf);
     92 						ii->admin_bind = false;
     93 						ii = NULL;
     94 					}
     95 					catch (const LDAPException &ex)
     96 					{
     97 						Log(this->owner) << "Unable to search for " << sf << ": " << ex.GetReason();
     98 					}
     99 				}
    100 				else
    101 				{
    102 					NickAlias *na = NickAlias::Find(ii->req->GetAccount());
    103 					if (na == NULL)
    104 					{
    105 						na = new NickAlias(ii->req->GetAccount(), new NickCore(ii->req->GetAccount()));
    106 						na->last_realname = ii->user ? ii->user->realname : ii->req->GetAccount();
    107 						FOREACH_MOD(OnNickRegister, (ii->user, na, ii->req->GetPassword()));
    108 						BotInfo *NickServ = Config->GetClient("NickServ");
    109 						if (ii->user && NickServ)
    110 							ii->user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->nick.c_str());
    111 					}
    112 					// encrypt and store the password in the nickcore
    113 					Anope::Encrypt(ii->req->GetPassword(), na->nc->pass);
    114 
    115 					na->nc->Extend<Anope::string>("m_ldap_authentication_dn", ii->dn);
    116 					ii->req->Success(me);
    117 				}
    118 				break;
    119 			}
    120 			default:
    121 				break;
    122 		}
    123 	}
    124 
    125 	void OnError(const LDAPResult &r) anope_override
    126 	{
    127 	}
    128 };
    129 
    130 class OnIdentifyInterface : public LDAPInterface
    131 {
    132 	Anope::string uid;
    133 
    134  public:
    135 	OnIdentifyInterface(Module *m, const Anope::string &i) : LDAPInterface(m), uid(i) { }
    136 
    137 	void OnDelete() anope_override
    138 	{
    139 		delete this;
    140 	}
    141 
    142 	void OnResult(const LDAPResult &r) anope_override
    143 	{
    144 		User *u = User::Find(uid);
    145 
    146 		if (!u || !u->Account() || r.empty())
    147 			return;
    148 
    149 		try
    150 		{
    151 			const LDAPAttributes &attr = r.get(0);
    152 			Anope::string email = attr.get(email_attribute);
    153 
    154 			if (!email.equals_ci(u->Account()->email))
    155 			{
    156 				u->Account()->email = email;
    157 				BotInfo *NickServ = Config->GetClient("NickServ");
    158 				if (NickServ)
    159 					u->SendMessage(NickServ, _("Your email has been updated to \002%s\002"), email.c_str());
    160 				Log(this->owner) << "Updated email address for " << u->nick << " (" << u->Account()->display << ") to " << email;
    161 			}
    162 		}
    163 		catch (const LDAPException &ex)
    164 		{
    165 			Log(this->owner) << ex.GetReason();
    166 		}
    167 	}
    168 
    169 	void OnError(const LDAPResult &r) anope_override
    170 	{
    171 		Log(this->owner) << r.error;
    172 	}
    173 };
    174 
    175 class OnRegisterInterface : public LDAPInterface
    176 {
    177  public:
    178 	OnRegisterInterface(Module *m) : LDAPInterface(m) { }
    179 
    180 	void OnResult(const LDAPResult &r) anope_override
    181 	{
    182 		Log(this->owner) << "Successfully added newly created account to LDAP";
    183 	}
    184 
    185 	void OnError(const LDAPResult &r) anope_override
    186 	{
    187 		Log(this->owner) << "Error adding newly created account to LDAP: " << r.getError();
    188 	}
    189 };
    190 
    191 class ModuleLDAPAuthentication : public Module
    192 {
    193 	ServiceReference<LDAPProvider> ldap;
    194 	OnRegisterInterface orinterface;
    195 
    196 	PrimitiveExtensibleItem<Anope::string> dn;
    197 
    198 	Anope::string password_attribute;
    199 	Anope::string disable_register_reason;
    200 	Anope::string disable_email_reason;
    201  public:
    202 	ModuleLDAPAuthentication(const Anope::string &modname, const Anope::string &creator) :
    203 		Module(modname, creator, EXTRA | VENDOR), ldap("LDAPProvider", "ldap/main"), orinterface(this),
    204 		dn(this, "m_ldap_authentication_dn")
    205 	{
    206 		me = this;
    207 	}
    208 
    209 	void Prioritize() anope_override
    210 	{
    211 		ModuleManager::SetPriority(this, PRIORITY_FIRST);
    212 	}
    213 
    214 	void OnReload(Configuration::Conf *config) anope_override
    215 	{
    216 		Configuration::Block *conf = Config->GetModule(this);
    217 
    218 		basedn = conf->Get<const Anope::string>("basedn");
    219 		search_filter = conf->Get<const Anope::string>("search_filter");
    220 		object_class = conf->Get<const Anope::string>("object_class");
    221 		username_attribute = conf->Get<const Anope::string>("username_attribute");
    222 		this->password_attribute = conf->Get<const Anope::string>("password_attribute");
    223 		email_attribute = conf->Get<const Anope::string>("email_attribute");
    224 		this->disable_register_reason = conf->Get<const Anope::string>("disable_register_reason");
    225 		this->disable_email_reason = conf->Get<const Anope::string>("disable_email_reason");
    226 
    227 		if (!email_attribute.empty())
    228 			/* Don't complain to users about how they need to update their email, we will do it for them */
    229 			config->GetModule("nickserv")->Set("forceemail", "false");
    230 	}
    231 
    232 	EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
    233 	{
    234 		if (!this->disable_register_reason.empty())
    235 		{
    236 			if (command->name == "nickserv/register" || command->name == "nickserv/group")
    237 			{
    238 				source.Reply(this->disable_register_reason);
    239 				return EVENT_STOP;
    240 			}
    241 		}
    242 
    243 		if (!email_attribute.empty() && !this->disable_email_reason.empty() && command->name == "nickserv/set/email")
    244 		{
    245 			source.Reply(this->disable_email_reason);
    246 			return EVENT_STOP;
    247 		}
    248 
    249 		return EVENT_CONTINUE;
    250 	}
    251 
    252 	void OnCheckAuthentication(User *u, IdentifyRequest *req) anope_override
    253 	{
    254 		if (!this->ldap)
    255 			return;
    256 
    257 		IdentifyInfo *ii = new IdentifyInfo(u, req, this->ldap);
    258 		this->ldap->BindAsAdmin(new IdentifyInterface(this, ii));
    259 	}
    260 
    261 	void OnNickIdentify(User *u) anope_override
    262 	{
    263 		if (email_attribute.empty() || !this->ldap)
    264 			return;
    265 
    266 		Anope::string *d = dn.Get(u->Account());
    267 		if (!d || d->empty())
    268 			return;
    269 
    270 		this->ldap->Search(new OnIdentifyInterface(this, u->GetUID()), *d, "(" + email_attribute + "=*)");
    271 	}
    272 
    273 	void OnNickRegister(User *, NickAlias *na, const Anope::string &pass) anope_override
    274 	{
    275 		if (!this->disable_register_reason.empty() || !this->ldap)
    276 			return;
    277 
    278 		this->ldap->BindAsAdmin(NULL);
    279 
    280 		LDAPMods attributes;
    281 		attributes.resize(4);
    282 
    283 		attributes[0].name = "objectClass";
    284 		attributes[0].values.push_back("top");
    285 		attributes[0].values.push_back(object_class);
    286 
    287 		attributes[1].name = username_attribute;
    288 		attributes[1].values.push_back(na->nick);
    289 
    290 		if (!na->nc->email.empty())
    291 		{
    292 			attributes[2].name = email_attribute;
    293 			attributes[2].values.push_back(na->nc->email);
    294 		}
    295 
    296 		attributes[3].name = this->password_attribute;
    297 		attributes[3].values.push_back(pass);
    298 
    299 		Anope::string new_dn = username_attribute + "=" + na->nick + "," + basedn;
    300 		this->ldap->Add(&this->orinterface, new_dn, attributes);
    301 	}
    302 };
    303 
    304 MODULE_INIT(ModuleLDAPAuthentication)