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)