diff options
author | Daniel Vassdal <shutter@canternet.org> | 2014-07-02 23:45:36 +0200 |
---|---|---|
committer | Attila Molnar <attilamolnar@hush.com> | 2014-08-04 13:36:18 +0200 |
commit | 4e0c477eb3333a7dab407f073313d21bb5027b02 (patch) | |
tree | 34f03a22a6f2e6991c61c30aea2007db4bcf2fb6 /src/modules | |
parent | 0fc6faa7a16d09dbfd4425fe48f95ae91bc3de15 (diff) |
m_pbkdf2: Added module
Bugfixes and minor changes are by @attilamolnar, original PR #767
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/m_pbkdf2.cpp | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/src/modules/m_pbkdf2.cpp b/src/modules/m_pbkdf2.cpp new file mode 100644 index 000000000..b66b3f058 --- /dev/null +++ b/src/modules/m_pbkdf2.cpp @@ -0,0 +1,260 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Daniel Vassdal <shutter@canternet.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/hash.h" + +// Format: +// Iterations:B64(Hash):B64(Salt) +// E.g. +// 10200:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +class PBKDF2Hash +{ + public: + unsigned int iterations; + unsigned int length; + std::string salt; + std::string hash; + + PBKDF2Hash(unsigned int itr, unsigned int dkl, const std::string& slt, const std::string& hsh = "") + : iterations(itr), length(dkl), salt(slt), hash(hsh) + { + } + + PBKDF2Hash(const std::string& data) + { + irc::sepstream ss(data, ':'); + std::string tok; + + ss.GetToken(tok); + this->iterations = ConvToInt(tok); + + ss.GetToken(tok); + this->hash = Base64ToBin(tok); + + ss.GetToken(tok); + this->salt = Base64ToBin(tok); + + this->length = this->hash.length(); + } + + std::string ToString() + { + if (!IsValid()) + return ""; + return ConvToStr(this->iterations) + ":" + BinToBase64(this->hash) + ":" + BinToBase64(this->salt); + } + + bool IsValid() + { + if (!this->iterations || !this->length || this->salt.empty() || this->hash.empty()) + return false; + return true; + } +}; + +class PBKDF2Provider : public HashProvider +{ + public: + HashProvider* provider; + unsigned int iterations; + unsigned int dkey_length; + + std::string PBKDF2(const std::string& pass, const std::string& salt, unsigned int itr = 0, unsigned int dkl = 0) + { + size_t blocks = std::ceil((double)dkl / provider->out_size); + + std::string output; + for (size_t block = 1; block <= blocks; block++) + { + char salt_data[4]; + for (size_t i = 0; i < sizeof(salt_data); i++) + salt_data[i] = block >> (24 - i * 8) & 0x0F; + + std::string salt_block(salt_data, 4); + salt_block = salt + salt_block; + + std::string blockdata = provider->hmac(pass, salt_block); + std::string lasthash = blockdata; + for (size_t iter = 1; iter < itr; iter++) + { + std::string tmphash = provider->hmac(pass, lasthash); + for (size_t i = 0; i < provider->out_size; i++) + blockdata[i] ^= tmphash[i]; + + lasthash = tmphash; + } + output += blockdata; + } + + output.erase(dkl); + return output; + } + + std::string GenerateRaw(const std::string& data) CXX11_OVERRIDE + { + PBKDF2Hash hs(this->iterations, this->dkey_length, ServerInstance->GenRandomStr(dkey_length, false)); + hs.hash = PBKDF2(data, hs.salt, this->iterations, this->dkey_length); + return hs.ToString(); + } + + bool Compare(const std::string& input, const std::string& hash) CXX11_OVERRIDE + { + PBKDF2Hash hs(hash); + if (!hs.IsValid()) + return false; + + std::string cmp = PBKDF2(input, hs.salt, hs.iterations, hs.length); + return (cmp == hs.hash); + } + + std::string ToPrintable(const std::string& raw) CXX11_OVERRIDE + { + return raw; + } + + PBKDF2Provider(Module* mod, HashProvider* hp) + : HashProvider(mod, "pbkdf2-hmac-" + hp->name.substr(hp->name.find('/') + 1)) + , provider(hp) + { + DisableAutoRegister(); + } +}; + +class ModulePBKDF2 : public Module +{ + std::vector<PBKDF2Provider*> providers; + + void GetConfig() + { + // First set the common values + ConfigTag* tag = ServerInstance->Config->ConfValue("pbkdf2"); + unsigned int global_iterations = tag->getInt("iterations", 12288, 1); + unsigned int global_dkey_length = tag->getInt("length", 32, 1, 1024); + for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ++i) + { + PBKDF2Provider* pi = *i; + pi->iterations = global_iterations; + pi->dkey_length = global_dkey_length; + } + + // Then the specific values + ConfigTagList tags = ServerInstance->Config->ConfTags("pbkdf2prov"); + for (ConfigIter i = tags.first; i != tags.second; ++i) + { + tag = i->second; + std::string hash_name = "hash/" + tag->getString("hash"); + for (std::vector<PBKDF2Provider*>::iterator j = providers.begin(); j != providers.end(); ++j) + { + PBKDF2Provider* pi = *j; + if (pi->provider->name != hash_name) + continue; + + pi->iterations = tag->getInt("iterations", global_iterations, 1); + pi->dkey_length = tag->getInt("length", global_dkey_length, 1, 1024); + } + } + } + + public: + ~ModulePBKDF2() + { + stdalgo::delete_all(providers); + } + + void Prioritize() CXX11_OVERRIDE + { + OnLoadModule(NULL); + } + + void OnLoadModule(Module* mod) CXX11_OVERRIDE + { + bool newProv = false; + // As the module doesn't tell us what ServiceProviders it has, let's iterate all (yay ...) the ServiceProviders + // Good thing people don't run loading and unloading those all the time + for (std::multimap<std::string, ServiceProvider*>::iterator i = ServerInstance->Modules->DataProviders.begin(); i != ServerInstance->Modules->DataProviders.end(); ++i) + { + ServiceProvider* provider = i->second; + + // Does the service belong to the new mod? + // In the case this is our first run (mod == NULL, continue anyway) + if (mod && provider->creator != mod) + continue; + + // Check if it's a hash provider + if (provider->name.compare(0, 5, "hash/")) + continue; + + HashProvider* hp = static_cast<HashProvider*>(provider); + + if (hp->IsKDF()) + continue; + + bool has_prov = false; + for (std::vector<PBKDF2Provider*>::const_iterator j = providers.begin(); j != providers.end(); ++j) + { + if ((*j)->provider == hp) + { + has_prov = true; + break; + } + } + if (has_prov) + continue; + + newProv = true; + + PBKDF2Provider* prov = new PBKDF2Provider(this, hp); + providers.push_back(prov); + ServerInstance->Modules->AddService(*prov); + } + + if (newProv) + GetConfig(); + } + + void OnUnloadModule(Module* mod) CXX11_OVERRIDE + { + for (std::vector<PBKDF2Provider*>::iterator i = providers.begin(); i != providers.end(); ) + { + PBKDF2Provider* item = *i; + if (item->provider->creator != mod) + { + ++i; + continue; + } + + ServerInstance->Modules->DelService(*item); + delete item; + i = providers.erase(i); + } + } + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE + { + GetConfig(); + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Implements PBKDF2 hashing", VF_VENDOR); + } +}; + +MODULE_INIT(ModulePBKDF2) |