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