]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sqlauth.cpp
c7c6c61a6ca0ffdee4add95263f78c5ddc23879d
[user/henk/code/inspircd.git] / src / modules / m_sqlauth.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "sql.h"
16 #include "hash.h"
17
18 /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */
19
20 enum AuthState {
21         AUTH_STATE_NONE = 0,
22         AUTH_STATE_BUSY = 1,
23         AUTH_STATE_FAIL = 2
24 };
25
26 class AuthQuery : public SQLQuery
27 {
28  public:
29         const std::string uid;
30         LocalIntExt& pendingExt;
31         bool verbose;
32         AuthQuery(Module* me, const std::string& db, const std::string& q, const std::string& u, LocalIntExt& e, bool v)
33                 : SQLQuery(me, db, q), uid(u), pendingExt(e), verbose(v)
34         {
35                 ServerInstance->Logs->Log("m_sqlauth",DEBUG, "SQLAUTH: db=%s query=\"%s\"", db.c_str(), q.c_str());
36         }
37         
38         void OnResult(SQLResult& res)
39         {
40                 User* user = ServerInstance->FindNick(uid);
41                 if (!user)
42                         return;
43                 if (res.Rows())
44                 {
45                         pendingExt.set(user, AUTH_STATE_NONE);
46                 }
47                 else
48                 {
49                         if (verbose)
50                                 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());
51                         pendingExt.set(user, AUTH_STATE_FAIL);
52                 }
53         }
54
55         void OnError(SQLerror& error)
56         {
57                 User* user = ServerInstance->FindNick(uid);
58                 if (!user)
59                         return;
60                 pendingExt.set(user, AUTH_STATE_FAIL);
61                 if (verbose)
62                         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());
63         }
64 };
65
66 class ModuleSQLAuth : public Module
67 {
68         LocalIntExt pendingExt;
69         dynamic_reference<SQLProvider> SQL;
70
71         std::string freeformquery;
72         std::string killreason;
73         std::string allowpattern;
74         std::string databaseid;
75         bool verbose;
76
77  public:
78         ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
79         {
80         }
81
82         void init()
83         {
84                 ServerInstance->Modules->AddService(pendingExt);
85                 OnRehash(NULL);
86                 Implementation eventlist[] = { I_OnUserDisconnect, I_OnCheckReady, I_OnRehash, I_OnUserRegister };
87                 ServerInstance->Modules->Attach(eventlist, this, 4);
88         }
89
90         void OnRehash(User* user)
91         {
92                 ConfigReader Conf;
93
94                 databaseid      = Conf.ReadValue("sqlauth", "dbid", 0);                 /* Database ID, given to the SQL service provider */
95                 freeformquery   = Conf.ReadValue("sqlauth", "query", 0);        /* Field name where username can be found */
96                 killreason      = Conf.ReadValue("sqlauth", "killreason", 0);   /* Reason to give when access is denied to a user (put your reg details here) */
97                 allowpattern    = Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */
98                 verbose         = Conf.ReadFlag("sqlauth", "verbose", 0);               /* Set to true if failed connects should be reported to operators */
99         }
100
101         ModResult OnUserRegister(LocalUser* user)
102         {
103                 // Note this is their initial (unresolved) connect block
104                 ConfigTag* tag = user->MyClass->config;
105                 if (!tag->getBool("usesqlauth", true))
106                         return MOD_RES_PASSTHRU;
107
108                 if (!allowpattern.empty() && InspIRCd::Match(user->nick,allowpattern))
109                         return MOD_RES_PASSTHRU;
110
111                 if (pendingExt.get(user))
112                         return MOD_RES_PASSTHRU;
113
114                 pendingExt.set(user, AUTH_STATE_BUSY);
115
116                 ParamM userinfo;
117                 SQL->PopulateUserInfo(user, userinfo);
118                 userinfo["pass"] = user->password;
119
120                 HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
121                 if (md5)
122                         userinfo["md5pass"] = md5->hexsum(user->password);
123
124                 HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
125                 if (sha256)
126                         userinfo["sha256pass"] = sha256->hexsum(user->password);
127
128                 SQL->submit(new AuthQuery(this, databaseid, SQL->FormatQuery(freeformquery, userinfo), user->uuid, pendingExt, verbose));
129
130                 return MOD_RES_PASSTHRU;
131         }
132
133         ModResult OnCheckReady(LocalUser* user)
134         {
135                 switch (pendingExt.get(user))
136                 {
137                         case AUTH_STATE_NONE:
138                                 return MOD_RES_PASSTHRU;
139                         case AUTH_STATE_BUSY:
140                                 return MOD_RES_DENY;
141                         case AUTH_STATE_FAIL:
142                                 ServerInstance->Users->QuitUser(user, killreason);
143                                 return MOD_RES_DENY;
144                 }
145                 return MOD_RES_PASSTHRU;
146         }
147
148         Version GetVersion()
149         {
150                 return Version("Allow/Deny connections based upon an arbitary SQL table", VF_VENDOR);
151         }
152 };
153
154 MODULE_INIT(ModuleSQLAuth)