]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_pbkdf2.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / modules / m_pbkdf2.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018, 2020 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
6  *   Copyright (C) 2014, 2020 Daniel Vassdal <shutter@canternet.org>
7  *   Copyright (C) 2014, 2016 Attila Molnar <attilamolnar@hush.com>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "modules/hash.h"
25
26 // Format:
27 // Iterations:B64(Hash):B64(Salt)
28 // E.g.
29 // 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
30 class PBKDF2Hash
31 {
32  public:
33         unsigned int iterations;
34         unsigned int length;
35         std::string salt;
36         std::string hash;
37
38         PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "")
39                 : iterations(itr), length(dkl), salt(slt), hash(hsh)
40         {
41         }
42
43         PBKDF2Hash(const std::string& data)
44         {
45                 irc::sepstream ss(data, ':');
46                 std::string tok;
47
48                 ss.GetToken(tok);
49                 this->iterations = ConvToNum<unsigned int>(tok);
50
51                 ss.GetToken(tok);
52                 this->hash = Base64ToBin(tok);
53
54                 ss.GetToken(tok);
55                 this->salt = Base64ToBin(tok);
56
57                 this->length = this->hash.length();
58         }
59
60         std::string ToString()
61         {
62                 if (!IsValid())
63                         return "";
64                 return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt);
65         }
66
67         bool IsValid()
68         {
69                 if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty())
70                         return false;
71                 return true;
72         }
73 };
74
75 class PBKDF2Provider : public HashProvider
76 {
77  public:
78         HashProvider* provider;
79         unsigned int iterations;
80         unsigned int dkey_length;
81
82         std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0)
83         {
84                 size_t blocks = std::ceil((double)dkl / provider->out_size);
85
86                 std::string output;
87                 std::string tmphash;
88                 std::string salt_block = salt;
89                 for (size_t block = 1; block <= blocks; block++)
90                 {
91                         char salt_data[4];
92                         for (size_t i = 0; i < sizeof(salt_data); i++)
93                                 salt_data[i] = block >> (24 - i * 8) & 0x0F;
94
95                         salt_block.erase(salt.length());
96                         salt_block.append(salt_data, sizeof(salt_data));
97
98                         std::string blockdata = provider->hmac(pass, salt_block);
99                         std::string lasthash = blockdata;
100                         for (size_t iter = 1; iter < itr; iter++)
101                         {
102                                 tmphash = provider->hmac(pass, lasthash);
103                                 for (size_t i = 0; i < provider->out_size; i++)
104                                         blockdata[i] ^= tmphash[i];
105
106                                 lasthash.swap(tmphash);
107                         }
108                         output += blockdata;
109                 }
110
111                 output.erase(dkl);
112                 return output;
113         }
114
115         std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE
116         {
117                 PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false));
118                 hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length);
119                 return hs.ToString();
120         }
121
122         bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE
123         {
124                 PBKDF2Hash hs(hash);
125                 if (!hs.IsValid())
126                         return false;
127
128                 std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length);
129                 return InspIRCd::TimingSafeCompare(cmp, hs.hash);
130         }
131
132         std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE
133         {
134                 return raw;
135         }
136
137         PBKDF2Provider(Module* mod, HashProvider* hp)
138                 : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1))
139                 , provider(hp)
140         {
141                 DisableAutoRegister();
142         }
143 };
144
145 struct ProviderConfig
146 {
147         unsigned long dkey_length;
148         unsigned long iterations;
149 };
150
151 typedef std::map<std::string, ProviderConfig> ProviderConfigMap;
152
153 class ModulePBKDF2 : public Module
154 {
155         std::vector<PBKDF2Provider*> providers;
156         ProviderConfig globalconfig;
157         ProviderConfigMap providerconfigs;
158
159         ProviderConfig GetConfigForProvider(const std::string& name) const
160         {
161                 ProviderConfigMap::const_iterator it = providerconfigs.find(name);
162                 if (it == providerconfigs.end())
163                         return globalconfig;
164
165                 return it->second;
166         }
167
168         void ConfigureProviders()
169         {
170                 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
171                 {
172                         PBKDF2Provider* pi = *i;
173                         ProviderConfig config = GetConfigForProvider(pi->name);
174                         pi->iterations = config.iterations;
175                         pi->dkey_length = config.dkey_length;
176                 }
177         }
178
179         void GetConfig()
180         {
181                 // First set the common values
182                 ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2");
183                 ProviderConfig newglobal;
184                 newglobal.iterations = tag->getUInt("iterations", 12288, 1);
185                 newglobal.dkey_length = tag->getUInt("length", 32, 1, 1024);
186
187                 // Then the specific values
188                 ProviderConfigMap newconfigs;
189                 ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov");
190                 for (ConfigIter i = tags.first; i != tags.second; ++i)
191                 {
192                         tag = i->second;
193                         std::string hash_name = "hash/" + tag->getString("hash");
194                         ProviderConfig& config = newconfigs[hash_name];
195
196                         config.iterations = tag->getUInt("iterations", newglobal.iterations, 1);
197                         config.dkey_length = tag->getUInt("length", newglobal.dkey_length, 1, 1024);
198                 }
199
200                 // Config is valid, apply it
201                 providerconfigs.swap(newconfigs);
202                 std::swap(globalconfig, newglobal);
203                 ConfigureProviders();
204         }
205
206  public:
207         ~ModulePBKDF2()
208         {
209                 stdalgo::delete_all(providers);
210         }
211
212         void init() CXX11_OVERRIDE
213         {
214                 // Let ourself know about any existing services.
215                 const ModuleManager::DataProviderMap& dataproviders = ServerInstance->Modules->DataProviders;
216                 for (ModuleManager::DataProviderMap::const_iterator it = dataproviders.begin(); it != dataproviders.end(); ++it)
217                         OnServiceAdd(*it->second);
218         }
219
220         void OnServiceAdd(ServiceProvider& provider) CXX11_OVERRIDE
221         {
222                 // Check if it's a hash provider
223                 if (provider.name.compare(0, 5, "hash/"))
224                         return;
225
226                 HashProvider* hp = static_cast<HashProvider*>(&provider);
227                 if (hp->IsKDF())
228                         return;
229
230                 PBKDF2Provider* prov = new PBKDF2Provider(this, hp);
231                 providers.push_back(prov);
232                 ServerInstance->Modules.AddService(*prov);
233
234                 ConfigureProviders();
235         }
236
237         void OnServiceDel(ServiceProvider& prov) CXX11_OVERRIDE
238         {
239                 for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i)
240                 {
241                         PBKDF2Provider* item = *i;
242                         if (item->provider != &prov)
243                                 continue;
244
245                         ServerInstance->Modules->DelService(*item);
246                         delete item;
247                         providers.erase(i);
248                         break;
249                 }
250         }
251
252         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
253         {
254                 GetConfig();
255         }
256
257         Version GetVersion() CXX11_OVERRIDE
258         {
259                 return Version("Allows other modules to generate PBKDF2 hashes.", VF_VENDOR);
260         }
261 };
262
263 MODULE_INIT(ModulePBKDF2)