]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sqlauth.cpp
Textual improvements and fixes such as typos, casing, etc. (#1612)
[user/henk/code/inspircd.git] / src / modules / m_sqlauth.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "modules/sql.h"
22 #include "modules/hash.h"
23 #include "modules/ssl.h"
24
25 enum AuthState {
26         AUTH_STATE_NONE = 0,
27         AUTH_STATE_BUSY = 1,
28         AUTH_STATE_FAIL = 2
29 };
30
31 class AuthQuery : public SQL::Query
32 {
33  public:
34         const std::string uid;
35         LocalIntExt& pendingExt;
36         bool verbose;
37         const std::string& kdf;
38         const std::string& pwcolumn;
39
40         AuthQuery(Module* me, const std::string& u, LocalIntExt& e, bool v, const std::string& kd, const std::string& pwcol)
41                 : SQL::Query(me)
42                 , uid(u)
43                 , pendingExt(e)
44                 , verbose(v)
45                 , kdf(kd)
46                 , pwcolumn(pwcol)
47         {
48         }
49
50         void OnResult(SQL::Result& res) CXX11_OVERRIDE
51         {
52                 LocalUser* user = static_cast<LocalUser*>(ServerInstance->FindUUID(uid));
53                 if (!user)
54                         return;
55
56                 if (res.Rows())
57                 {
58                         if (!kdf.empty())
59                         {
60                                 HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + kdf);
61                                 if (!hashprov)
62                                 {
63                                         if (verbose)
64                                                 ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (a provider for %s was not loaded)", user->GetFullRealHost().c_str(), kdf.c_str());
65                                         pendingExt.set(user, AUTH_STATE_FAIL);
66                                         return;
67                                 }
68
69                                 size_t colindex = 0;
70                                 if (!pwcolumn.empty() && !res.HasColumn(pwcolumn, colindex))
71                                 {
72                                         if (verbose)
73                                                 ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (the column specified (%s) was not returned)", user->GetFullRealHost().c_str(), pwcolumn.c_str());
74                                         pendingExt.set(user, AUTH_STATE_FAIL);
75                                         return;
76                                 }
77
78                                 SQL::Row row;
79                                 while (res.GetRow(row))
80                                 {
81                                         if (hashprov->Compare(user->password, row[colindex]))
82                                         {
83                                                 pendingExt.set(user, AUTH_STATE_NONE);
84                                                 return;
85                                         }
86                                 }
87
88                                 if (verbose)
89                                         ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (password from the SQL query did not match the user provided password)", user->GetFullRealHost().c_str());
90                                 pendingExt.set(user, AUTH_STATE_FAIL);
91                                 return;
92                         }
93
94                         pendingExt.set(user, AUTH_STATE_NONE);
95                 }
96                 else
97                 {
98                         if (verbose)
99                                 ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query returned no matches)", user->GetFullRealHost().c_str());
100                         pendingExt.set(user, AUTH_STATE_FAIL);
101                 }
102         }
103
104         void OnError(SQL::Error& error) CXX11_OVERRIDE
105         {
106                 User* user = ServerInstance->FindNick(uid);
107                 if (!user)
108                         return;
109                 pendingExt.set(user, AUTH_STATE_FAIL);
110                 if (verbose)
111                         ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL query failed: %s)", user->GetFullRealHost().c_str(), error.ToString());
112         }
113 };
114
115 class ModuleSQLAuth : public Module
116 {
117         LocalIntExt pendingExt;
118         dynamic_reference<SQL::Provider> SQL;
119         UserCertificateAPI sslapi;
120
121         std::string freeformquery;
122         std::string killreason;
123         std::string allowpattern;
124         bool verbose;
125         std::vector<std::string> hash_algos;
126         std::string kdf;
127         std::string pwcolumn;
128
129  public:
130         ModuleSQLAuth()
131                 : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
132                 , SQL(this, "SQL")
133                 , sslapi(this)
134         {
135         }
136
137         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
138         {
139                 ConfigTag* conf = ServerInstance->Config->ConfValue("sqlauth");
140                 std::string dbid = conf->getString("dbid");
141                 if (dbid.empty())
142                         SQL.SetProvider("SQL");
143                 else
144                         SQL.SetProvider("SQL/" + dbid);
145                 freeformquery = conf->getString("query");
146                 killreason = conf->getString("killreason");
147                 allowpattern = conf->getString("allowpattern");
148                 verbose = conf->getBool("verbose");
149                 kdf = conf->getString("kdf");
150                 pwcolumn = conf->getString("column");
151
152                 hash_algos.clear();
153                 irc::commasepstream algos(conf->getString("hash", "md5,sha256"));
154                 std::string algo;
155                 while (algos.GetToken(algo))
156                         hash_algos.push_back(algo);
157         }
158
159         ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
160         {
161                 // Note this is their initial (unresolved) connect block
162                 ConfigTag* tag = user->MyClass->config;
163                 if (!tag->getBool("usesqlauth", true))
164                         return MOD_RES_PASSTHRU;
165
166                 if (!allowpattern.empty() && InspIRCd::Match(user->nick,allowpattern))
167                         return MOD_RES_PASSTHRU;
168
169                 if (pendingExt.get(user))
170                         return MOD_RES_PASSTHRU;
171
172                 if (!SQL)
173                 {
174                         ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s (SQL database not present)", user->GetFullRealHost().c_str());
175                         ServerInstance->Users->QuitUser(user, killreason);
176                         return MOD_RES_PASSTHRU;
177                 }
178
179                 pendingExt.set(user, AUTH_STATE_BUSY);
180
181                 SQL::ParamMap userinfo;
182                 SQL::PopulateUserInfo(user, userinfo);
183                 userinfo["pass"] = user->password;
184                 userinfo["certfp"] = sslapi ? sslapi->GetFingerprint(user) : "";
185
186                 for (std::vector<std::string>::const_iterator it = hash_algos.begin(); it != hash_algos.end(); ++it)
187                 {
188                         HashProvider* hashprov = ServerInstance->Modules->FindDataService<HashProvider>("hash/" + *it);
189                         if (hashprov && !hashprov->IsKDF())
190                                 userinfo[*it + "pass"] = hashprov->Generate(user->password);
191                 }
192
193                 SQL->Submit(new AuthQuery(this, user->uuid, pendingExt, verbose, kdf, pwcolumn), freeformquery, userinfo);
194
195                 return MOD_RES_PASSTHRU;
196         }
197
198         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
199         {
200                 switch (pendingExt.get(user))
201                 {
202                         case AUTH_STATE_NONE:
203                                 return MOD_RES_PASSTHRU;
204                         case AUTH_STATE_BUSY:
205                                 return MOD_RES_DENY;
206                         case AUTH_STATE_FAIL:
207                                 ServerInstance->Users->QuitUser(user, killreason);
208                                 return MOD_RES_DENY;
209                 }
210                 return MOD_RES_PASSTHRU;
211         }
212
213         Version GetVersion() CXX11_OVERRIDE
214         {
215                 return Version("Allow/deny connections based upon an arbitrary SQL table", VF_VENDOR);
216         }
217 };
218
219 MODULE_INIT(ModuleSQLAuth)