/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2011 InspIRCd Development Team * See: http://wiki.inspircd.org/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "account.h" #include "sql.h" #include "hash.h" /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */ enum AuthState { AUTH_STATE_NONE = 0, AUTH_STATE_BUSY = 1, AUTH_STATE_FAIL = 2, AUTH_STATE_OK = 3 }; static dynamic_reference account("account"); class AuthQuery : public SQLQuery { public: const std::string uid; LocalIntExt& pendingExt; bool verbose; AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v) : SQLQuery(me), uid(u), pendingExt(e), verbose(v) { } void OnResult(SQLResult& res) { User* user = ServerInstance->FindNick(uid); if (!user) return; SQLEntries result; if (res.GetRow(result)) { std::vector cols; res.GetCols(cols); std::string acct, tag; for(unsigned int i=0; i < cols.size(); i++) { if (result[i].nul) continue; if (cols[i] == "account") acct = result[i].value; if (cols[i] == "tag") tag = result[i].value; if (cols[i] == "class") ServerInstance->ForcedClass.set(user, result[i].value); } if (!acct.empty() && account) { account->DoLogin(user, acct, tag); } pendingExt.set(user, AUTH_STATE_OK); } else { if (verbose) ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick.c_str(), user->ident.c_str(), user->host.c_str()); pendingExt.set(user, AUTH_STATE_FAIL); } } void OnError(SQLerror& error) { User* user = ServerInstance->FindNick(uid); if (!user) return; pendingExt.set(user, AUTH_STATE_FAIL); if (verbose) ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), error.Str()); } }; class ModuleSQLAuth : public Module { LocalIntExt pendingExt; dynamic_reference SQL; std::string freeformquery; bool verbose; public: ModuleSQLAuth() : pendingExt(EXTENSIBLE_USER, "sqlauth-wait", this), SQL("SQL") { } void init() { ServerInstance->Modules->AddService(pendingExt); Implementation eventlist[] = { I_OnCheckReady, I_OnUserRegister, I_OnSetConnectClass }; ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); } void ReadConfig(ConfigReadStatus&) { ConfigTag* conf = ServerInstance->Config->GetTag("sqlauth"); std::string dbid = conf->getString("dbid"); if (dbid.empty()) SQL.SetProvider("SQL"); else SQL.SetProvider("SQL/" + dbid); freeformquery = conf->getString("query"); verbose = conf->getBool("verbose"); } void OnUserRegister(LocalUser* user) { // Note this is their initial (unresolved) connect block ConfigTag* tag = user->MyClass->config; if (!tag->getBool("usesqlauth", true)) return; if (pendingExt.get(user)) return; if (!SQL) { ServerInstance->SNO->WriteGlobalSno('a', "SQLAUTH: No database present, connection denied"); pendingExt.set(user, AUTH_STATE_FAIL); return; } pendingExt.set(user, AUTH_STATE_BUSY); ParamM userinfo; user->PopulateInfoMap(userinfo); userinfo["pass"] = user->password; HashProvider* md5 = ServerInstance->Modules->FindDataService("hash/md5"); if (md5) userinfo["md5pass"] = md5->hexsum(user->password); HashProvider* sha256 = ServerInstance->Modules->FindDataService("hash/sha256"); if (sha256) userinfo["sha256pass"] = sha256->hexsum(user->password); SQL->submit(new AuthQuery(this, user->uuid, pendingExt, verbose), freeformquery, userinfo); } ModResult OnCheckReady(LocalUser* user) { if (pendingExt.get(user) == AUTH_STATE_BUSY) return MOD_RES_DENY; return MOD_RES_PASSTHRU; } ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) { if (myclass->config->getBool("requiresqlauth", false) && pendingExt.get(user) == AUTH_STATE_FAIL) return MOD_RES_DENY; return MOD_RES_PASSTHRU; } Version GetVersion() { return Version("Allow/Deny connections based upon an arbitary SQL table", VF_VENDOR); } }; MODULE_INIT(ModuleSQLAuth)