1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd is copyright (C) 2002-2004 ChatSpike-Dev.
7 * <brain@chatspike.net>
8 * <Craig@chatspike.net>
11 * Written by Craig Edwards, Craig McLure, and others.
12 * This program is free but copyrighted software; see
13 * the file COPYING for details.
15 * ---------------------------------------------------
25 #include "helperfuncs.h"
28 /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */
30 typedef std::map<unsigned int, userrec*> QueryUserMap;
32 class ModuleSQLAuth : public Module
36 std::string usertable;
37 std::string userfield;
38 std::string passfield;
39 std::string encryption;
40 std::string killreason;
41 std::string allowpattern;
42 std::string databaseid;
49 ModuleSQLAuth(Server* Me)
56 void Implements(char* List)
58 List[I_OnUserDisconnect] = List[I_OnCheckReady] = List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = 1;
61 virtual void OnRehash(const std::string ¶meter)
65 usertable = Conf.ReadValue("sqlauth", "usertable", 0); /* User table name */
66 databaseid = Conf.ReadValue("sqlauth", "dbid", 0); /* Database ID, given to the SQL service provider */
67 userfield = Conf.ReadValue("sqlauth", "userfield", 0); /* Field name where username can be found */
68 passfield = Conf.ReadValue("sqlauth", "passfield", 0); /* Field name where password can be found */
69 killreason = Conf.ReadValue("sqlauth", "killreason", 0); /* Reason to give when access is denied to a user (put your reg details here) */
70 allowpattern= Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */
71 encryption = Conf.ReadValue("sqlauth", "encryption", 0); /* Name of sql function used to encrypt password, e.g. "md5" or "passwd".
72 * define, but leave blank if no encryption is to be used.
74 verbose = Conf.ReadFlag("sqlauth", "verbose", 0); /* Set to true if failed connects should be reported to operators */
76 if (encryption.find("(") == std::string::npos)
78 encryption.append("(");
82 virtual void OnUserRegister(userrec* user)
84 if ((allowpattern != "") && (Srv->MatchText(user->nick,allowpattern)))
87 if (!CheckCredentials(user))
90 WriteOpers("Forbidden connection from %s!%s@%s (invalid login/password)",user->nick,user->ident,user->host);
91 Srv->QuitUser(user,killreason);
95 bool CheckCredentials(userrec* user)
101 target = Srv->FindFeature("SQL");
105 SQLrequest req = SQLreq(this, target, databaseid, "SELECT ? FROM ? WHERE ? = '?' AND ? = ?'?')", userfield, usertable, userfield, user->nick, passfield, encryption, user->password);
109 /* When we get the query response from the service provider we will be given an ID to play with,
110 * just an ID number which is unique to this query. We need a way of associating that ID with a userrec
111 * so we insert it into a map mapping the IDs to users.
112 * This isn't quite enough though, as if the user quit while the query was in progress then when the result
113 * came to be processed we'd get an invalid userrec* out of the map. Now we *could* solve this by watching
114 * OnUserDisconnect() and iterating the map every time someone quits to make sure they didn't have any queries
115 * in progress, but that would be relatively slow and inefficient. Instead (thanks to w00t ;p) we attach a list
116 * of query IDs associated with it to the userrec, so in OnUserDisconnect() we can remove it immediately.
118 log(DEBUG, "Sent query, got given ID %lu", req.id);
119 qumap.insert(std::make_pair(req.id, user));
121 if(!user->Extend("sqlauth_queryid", new unsigned long(req.id)))
123 log(DEBUG, "BUG: user being sqlauth'd already extended with 'sqlauth_queryid' :/");
130 log(DEBUG, "SQLrequest failed: %s", req.error.Str());
133 WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str());
140 log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be allowed to connect until it comes back unless they match an exception");
145 virtual char* OnRequest(Request* request)
147 if(strcmp(SQLRESID, request->GetData()) == 0)
150 QueryUserMap::iterator iter;
152 res = static_cast<SQLresult*>(request);
154 log(DEBUG, "Got SQL result (%s) with ID %lu", res->GetData(), res->id);
156 iter = qumap.find(res->id);
158 if(iter != qumap.end())
165 log(DEBUG, "Associated query ID %lu with user %s", res->id, user->nick);
167 log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols());
171 /* We got a row in the result, this is enough really */
172 user->Extend("sqlauthed");
176 /* No rows in result, this means there was no record matching the user */
177 WriteOpers("Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick, user->ident, user->host);
180 /* Remove our ID from the lookup table to keep it as small and neat as possible */
183 /* Cleanup the userrec, no point leaving this here */
184 if(user->GetExt("sqlauth_queryid", id))
186 user->Shrink("sqlauth_queryid");
192 log(DEBUG, "Got query with unknown ID, this probably means the user quit while the query was in progress");
198 log(DEBUG, "Got unsupported API version string: %s", request->GetData());
203 virtual void OnUserDisconnect(userrec* user)
207 if(user->GetExt("sqlauth_queryid", id))
209 QueryUserMap::iterator iter;
211 iter = qumap.find(*id);
213 if(iter != qumap.end())
215 if(iter->second == user)
219 log(DEBUG, "Erased query from map associated with quitting user %s", user->nick);
223 log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map");
228 log(DEBUG, "BUG: user %s was extended with sqlauth_queryid but there was nothing matching in the map", user->nick);
231 user->Shrink("sqlauth_queryid");
236 virtual bool OnCheckReady(userrec* user)
238 return user->GetExt("sqlauthed");
241 virtual ~ModuleSQLAuth()
245 virtual Version GetVersion()
247 return Version(1,0,1,0,VF_VENDOR);
252 class ModuleSQLAuthFactory : public ModuleFactory
255 ModuleSQLAuthFactory()
259 ~ModuleSQLAuthFactory()
263 virtual Module * CreateModule(Server* Me)
265 return new ModuleSQLAuth(Me);
271 extern "C" void * init_module( void )
273 return new ModuleSQLAuthFactory;