summaryrefslogtreecommitdiff
path: root/include/modules/nickserv/sasl.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/modules/nickserv/sasl.h')
-rw-r--r--include/modules/nickserv/sasl.h188
1 files changed, 188 insertions, 0 deletions
diff --git a/include/modules/nickserv/sasl.h b/include/modules/nickserv/sasl.h
new file mode 100644
index 000000000..f23a5d32a
--- /dev/null
+++ b/include/modules/nickserv/sasl.h
@@ -0,0 +1,188 @@
+/*
+ *
+ * (C) 2014-2025 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ */
+
+#pragma once
+
+namespace SASL
+{
+ struct Message final
+ {
+ Anope::string source;
+ Anope::string target;
+ Anope::string type;
+ std::vector<Anope::string> data;
+ };
+
+ class Mechanism;
+ struct Session;
+
+ class Service
+ : public ::Service
+ {
+ public:
+ Service(Module *o) : ::Service(o, "SASL::Service", "sasl") { }
+
+ virtual void ProcessMessage(const Message &) = 0;
+
+ virtual Session *GetSession(const Anope::string &uid) = 0;
+
+ virtual void SendMessage(SASL::Session *session, const Anope::string &type, const Anope::string &data) = 0;
+
+ virtual void Succeed(Session *, NickCore *) = 0;
+ virtual void Fail(Session *) = 0;
+ virtual void DeleteSessions(Mechanism *, bool = false) = 0;
+ virtual void RemoveSession(Session *) = 0;
+ };
+
+ static ServiceReference<SASL::Service> service("SASL::Service", "sasl");
+
+ struct Session
+ {
+ time_t created;
+ Anope::string uid;
+ Anope::string hostname, ip;
+ Reference<Mechanism> mech;
+
+ Session(Mechanism *m, const Anope::string &u) : created(Anope::CurTime), uid(u), mech(m) { }
+
+ inline Anope::string GetUserInfo()
+ {
+ auto *u = User::Find(uid);
+ if (u)
+ return u->GetMask();
+ if (!hostname.empty() && !ip.empty())
+ return Anope::printf("%s (%s)", hostname.c_str(), ip.c_str());
+ return "A user";
+ };
+
+ virtual ~Session()
+ {
+ if (service)
+ service->RemoveSession(this);
+ }
+ };
+
+ /* PLAIN, EXTERNAL, etc */
+ class Mechanism
+ : public ::Service
+ {
+ public:
+ Mechanism(Module *o, const Anope::string &sname) : Service(o, "SASL::Mechanism", sname) { }
+
+ virtual Session *CreateSession(const Anope::string &uid) { return new Session(this, uid); }
+
+ virtual bool ProcessMessage(Session *session, const Message &) = 0;
+
+ virtual ~Mechanism()
+ {
+ if (service)
+ service->DeleteSessions(this, true);
+ }
+ };
+
+ class IdentifyRequest
+ : public ::IdentifyRequest
+ {
+ Anope::string uid;
+ Anope::string hostname;
+
+ inline Anope::string GetUserInfo()
+ {
+ auto *u = User::Find(uid);
+ if (u)
+ return u->GetMask();
+ if (!hostname.empty() && !GetAddress().empty())
+ return Anope::printf("%s (%s)", hostname.c_str(), GetAddress().c_str());
+ return "A user";
+ };
+
+ public:
+ IdentifyRequest(Module *m, const Anope::string &id, const Anope::string &acc, const Anope::string &pass, const Anope::string &h, const Anope::string &i)
+ : ::IdentifyRequest(m, acc, pass, i)
+ , uid(id)
+ , hostname(h)
+ {
+ }
+
+ void OnSuccess() override
+ {
+ if (!service)
+ return;
+
+ NickAlias *na = NickAlias::Find(GetAccount());
+ if (!na || na->nc->HasExt("NS_SUSPENDED") || na->nc->HasExt("UNCONFIRMED"))
+ return OnFail();
+
+ unsigned int maxlogins = Config->GetModule("ns_identify").Get<unsigned int>("maxlogins");
+ if (maxlogins && na->nc->users.size() >= maxlogins)
+ return OnFail();
+
+ Session *s = service->GetSession(uid);
+ if (s)
+ {
+ Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " identified to account " << this->GetAccount() << " using SASL";
+ service->Succeed(s, na->nc);
+ delete s;
+ }
+ }
+
+ void OnFail() override
+ {
+ if (!service)
+ return;
+
+ Session *s = service->GetSession(uid);
+ if (s)
+ {
+ service->Fail(s);
+ delete s;
+ }
+
+ Anope::string accountstatus;
+ NickAlias *na = NickAlias::Find(GetAccount());
+ if (!na)
+ accountstatus = "nonexistent ";
+ else if (na->nc->HasExt("NS_SUSPENDED"))
+ accountstatus = "suspended ";
+ else if (na->nc->HasExt("UNCONFIRMED"))
+ accountstatus = "unconfirmed ";
+
+ Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " failed to identify for " << accountstatus << "account " << this->GetAccount() << " using SASL";
+ }
+ };
+
+ /** Sends IRCd messages used by the SASL module. */
+ class ProtocolInterface
+ : public ::Service
+ {
+ protected:
+ ProtocolInterface(Module *o)
+ : ::Service(o, "SASL::ProtocolInterface", "sasl")
+ {
+ }
+
+ public:
+ /** Sends the list of SASL mechanisms to the IRCd
+ * @param mechs The list of SASL mechanisms.
+ */
+ virtual void SendSASLMechanisms(std::vector<Anope::string> &mechs) { };
+
+ /** Sends a SASL message to the IRCd.
+ * @param message The SASL message to send.
+ */
+ virtual void SendSASLMessage(const SASL::Message &message) = 0;
+
+ /** Sends a login or logout for \p uid to \p na.
+ * @param uid The uid of the user to log in.
+ * @param na The nick alias to log the user in as or logout if nullptr.
+ */
+ virtual void SendSVSLogin(const Anope::string &uid, NickAlias *na) = 0;
+ };
+
+ static ServiceReference<SASL::ProtocolInterface> protocol_interface("SASL::ProtocolInterface", "sasl");
+}