/* +------------------------------------+
* | 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<AccountProvider> 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<std::string> 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<SQLProvider> 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<HashProvider>("hash/md5");
if (md5)
userinfo["md5pass"] = md5->hexsum(user->password);
HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("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)