anope

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

m_sasl.cpp (7788B)

      1 /*
      2  *
      3  * (C) 2014-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/sasl.h"
     11 #include "modules/ns_cert.h"
     12 
     13 using namespace SASL;
     14 
     15 class Plain : public Mechanism
     16 {
     17  public:
     18 	Plain(Module *o) : Mechanism(o, "PLAIN") { }
     19 
     20 	void ProcessMessage(Session *sess, const SASL::Message &m) anope_override
     21 	{
     22 		if (m.type == "S")
     23 		{
     24 			sasl->SendMessage(sess, "C", "+");
     25 		}
     26 		else if (m.type == "C")
     27 		{
     28 			Anope::string decoded;
     29 			Anope::B64Decode(m.data, decoded);
     30 
     31 			size_t p = decoded.find('\0');
     32 			if (p == Anope::string::npos)
     33 			{
     34 				sasl->Fail(sess);
     35 				delete sess;
     36 				return;
     37 			}
     38 			decoded = decoded.substr(p + 1);
     39 
     40 			p = decoded.find('\0');
     41 			if (p == Anope::string::npos)
     42 			{
     43 				sasl->Fail(sess);
     44 				delete sess;
     45 				return;
     46 			}
     47 
     48 			Anope::string acc = decoded.substr(0, p),
     49 				pass = decoded.substr(p + 1);
     50 
     51 			if (acc.empty() || pass.empty() || !IRCD->IsNickValid(acc) || pass.find_first_of("\r\n") != Anope::string::npos)
     52 			{
     53 				sasl->Fail(sess);
     54 				delete sess;
     55 				return;
     56 			}
     57 
     58 			SASL::IdentifyRequest *req = new SASL::IdentifyRequest(this->owner, m.source, acc, pass, sess->hostname, sess->ip);
     59 			FOREACH_MOD(OnCheckAuthentication, (NULL, req));
     60 			req->Dispatch();
     61 		}
     62 	}
     63 };
     64 
     65 class External : public Mechanism
     66 {
     67 	ServiceReference<CertService> certs;
     68 
     69 	struct Session : SASL::Session
     70 	{
     71 		Anope::string cert;
     72 
     73 		Session(Mechanism *m, const Anope::string &u) : SASL::Session(m, u) { }
     74 	};
     75 
     76  public:
     77 	External(Module *o) : Mechanism(o, "EXTERNAL"), certs("CertService", "certs")
     78 	{
     79 		if (!IRCD || !IRCD->CanCertFP)
     80 			throw ModuleException("No CertFP");
     81 	}
     82 
     83 	Session* CreateSession(const Anope::string &uid) anope_override
     84 	{
     85 		return new Session(this, uid);
     86 	}
     87 
     88 	void ProcessMessage(SASL::Session *sess, const SASL::Message &m) anope_override
     89 	{
     90 		Session *mysess = anope_dynamic_static_cast<Session *>(sess);
     91 
     92 		if (m.type == "S")
     93 		{
     94 			mysess->cert = m.ext;
     95 
     96 			sasl->SendMessage(sess, "C", "+");
     97 		}
     98 		else if (m.type == "C")
     99 		{
    100 			if (!certs || mysess->cert.empty())
    101 			{
    102 				sasl->Fail(sess);
    103 				delete sess;
    104 				return;
    105 			}
    106 
    107 			Anope::string user = "A user";
    108 			if (!mysess->hostname.empty() && !mysess->ip.empty())
    109 				user = mysess->hostname + " (" + mysess->ip + ")";
    110 
    111 			NickCore *nc = certs->FindAccountFromCert(mysess->cert);
    112 			if (!nc || nc->HasExt("NS_SUSPENDED") || nc->HasExt("UNCONFIRMED"))
    113 			{
    114 				Log(this->owner, "sasl", Config->GetClient("NickServ")) << user << " failed to identify using certificate " << mysess->cert << " using SASL EXTERNAL";
    115 				sasl->Fail(sess);
    116 				delete sess;
    117 				return;
    118 			}
    119 
    120 			Log(this->owner, "sasl", Config->GetClient("NickServ")) << user << " identified to account " << nc->display << " using SASL EXTERNAL";
    121 			sasl->Succeed(sess, nc);
    122 			delete sess;
    123 		}
    124 	}
    125 };
    126 
    127 class SASLService : public SASL::Service, public Timer
    128 {
    129 	std::map<Anope::string, SASL::Session *> sessions;
    130 
    131  public:
    132 	SASLService(Module *o) : SASL::Service(o), Timer(o, 60, Anope::CurTime, true) { }
    133 
    134 	~SASLService()
    135 	{
    136 		for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end(); it++)
    137 			delete it->second;
    138 	}
    139 
    140 	void ProcessMessage(const SASL::Message &m) anope_override
    141 	{
    142 		if (m.target != "*")
    143 		{
    144 			Server *s = Server::Find(m.target);
    145 			if (s != Me)
    146 			{
    147 				User *u = User::Find(m.target);
    148 				if (!u || u->server != Me)
    149 					return;
    150 			}
    151 		}
    152 
    153 		Session* session = GetSession(m.source);
    154 
    155 		if (m.type == "S")
    156 		{
    157 			ServiceReference<Mechanism> mech("SASL::Mechanism", m.data);
    158 			if (!mech)
    159 			{
    160 				Session tmp(NULL, m.source);
    161 
    162 				sasl->SendMechs(&tmp);
    163 				sasl->Fail(&tmp);
    164 				return;
    165 			}
    166 
    167 			Anope::string hostname, ip;
    168 			if (session)
    169 			{
    170 				// Copy over host/ip to mech-specific session
    171 				hostname = session->hostname;
    172 				ip = session->ip;
    173 				delete session;
    174 			}
    175 
    176 			session = mech->CreateSession(m.source);
    177 			if (session)
    178 			{
    179 				session->hostname = hostname;
    180 				session->ip = ip;
    181 
    182 				sessions[m.source] = session;
    183 			}
    184 		}
    185 		else if (m.type == "D")
    186 		{
    187 			delete session;
    188 			return;
    189 		}
    190 		else if (m.type == "H")
    191 		{
    192 			if (!session)
    193 			{
    194 				session = new Session(NULL, m.source);
    195 				sessions[m.source] = session;
    196 			}
    197 			session->hostname = m.data;
    198 			session->ip = m.ext;
    199 		}
    200 
    201 		if (session && session->mech)
    202 			session->mech->ProcessMessage(session, m);
    203 	}
    204 
    205 	Anope::string GetAgent() anope_override
    206 	{
    207 		Anope::string agent = Config->GetModule(Service::owner)->Get<Anope::string>("agent", "NickServ");
    208 		BotInfo *bi = Config->GetClient(agent);
    209 		if (bi)
    210 			agent = bi->GetUID();
    211 		return agent;
    212 	}
    213 
    214 	Session* GetSession(const Anope::string &uid) anope_override
    215 	{
    216 		std::map<Anope::string, Session *>::iterator it = sessions.find(uid);
    217 		if (it != sessions.end())
    218 			return it->second;
    219 		return NULL;
    220 	}
    221 
    222 	void RemoveSession(Session *sess) anope_override
    223 	{
    224 		sessions.erase(sess->uid);
    225 	}
    226 
    227 	void DeleteSessions(Mechanism *mech, bool da) anope_override
    228 	{
    229 		for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
    230 		{
    231 			std::map<Anope::string, Session *>::iterator del = it++;
    232 			if (*del->second->mech == mech)
    233 			{
    234 				if (da)
    235 					this->SendMessage(del->second, "D", "A");
    236 				delete del->second;
    237 			}
    238 		}
    239 	}
    240 
    241 	void SendMessage(Session *session, const Anope::string &mtype, const Anope::string &data) anope_override
    242 	{
    243 		SASL::Message msg;
    244 		msg.source = this->GetAgent();
    245 		msg.target = session->uid;
    246 		msg.type = mtype;
    247 		msg.data = data;
    248 
    249 		IRCD->SendSASLMessage(msg);
    250 	}
    251 
    252 	void Succeed(Session *session, NickCore *nc) anope_override
    253 	{
    254 		// If the user is already introduced then we log them in now.
    255 		// Otherwise, we send an SVSLOGIN to log them in later.
    256 		User *user = User::Find(session->uid);
    257 		NickAlias *na = NickAlias::Find(nc->display);
    258 		if (user)
    259 		{
    260 			user->Identify(na);
    261 		}
    262 		else
    263 		{
    264 			IRCD->SendSVSLogin(session->uid, nc->display, na->GetVhostIdent(), na->GetVhostHost());
    265 		}
    266 		this->SendMessage(session, "D", "S");
    267 	}
    268 
    269 	void Fail(Session *session) anope_override
    270 	{
    271 		this->SendMessage(session, "D", "F");
    272 	}
    273 
    274 	void SendMechs(Session *session) anope_override
    275 	{
    276 		std::vector<Anope::string> mechs = Service::GetServiceKeys("SASL::Mechanism");
    277 		Anope::string buf;
    278 		for (unsigned j = 0; j < mechs.size(); ++j)
    279 			buf += "," + mechs[j];
    280 
    281 		this->SendMessage(session, "M", buf.empty() ? "" : buf.substr(1));
    282 	}
    283 
    284 	void Tick(time_t) anope_override
    285 	{
    286 		for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
    287 		{
    288 			Anope::string key = it->first;
    289 			Session *s = it->second;
    290 			++it;
    291 
    292 			if (!s || s->created + 60 < Anope::CurTime)
    293 			{
    294 				delete s;
    295 				sessions.erase(key);
    296 			}
    297 		}
    298 	}
    299 };
    300 
    301 class ModuleSASL : public Module
    302 {
    303 	SASLService sasl;
    304 
    305 	Plain plain;
    306 	External *external;
    307 
    308 	std::vector<Anope::string> mechs;
    309 
    310 	void CheckMechs()
    311 	{
    312 		std::vector<Anope::string> newmechs = ::Service::GetServiceKeys("SASL::Mechanism");
    313 		if (newmechs == mechs)
    314 			return;
    315 
    316 		mechs = newmechs;
    317 
    318 		// If we are connected to the network then broadcast the mechlist.
    319 		if (Me && Me->IsSynced())
    320 			IRCD->SendSASLMechanisms(mechs);
    321 	}
    322 
    323  public:
    324 	ModuleSASL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    325 		sasl(this), plain(this), external(NULL)
    326 	{
    327 		try
    328 		{
    329 			external = new External(this);
    330 			CheckMechs();
    331 		}
    332 		catch (ModuleException &) { }
    333 	}
    334 
    335 	~ModuleSASL()
    336 	{
    337 		delete external;
    338 	}
    339 
    340 	void OnModuleLoad(User *, Module *) anope_override
    341 	{
    342 		CheckMechs();
    343 	}
    344 
    345 	void OnModuleUnload(User *, Module *) anope_override
    346 	{
    347 		CheckMechs();
    348 	}
    349 
    350 	void OnPreUplinkSync(Server *) anope_override
    351 	{
    352 		// We have not yet sent a mechanism list so always do it here.
    353 		IRCD->SendSASLMechanisms(mechs);
    354 	}
    355 };
    356 
    357 MODULE_INIT(ModuleSASL)