diff options
author | Daniel Vassdal <shutter@canternet.org> | 2014-02-16 10:26:58 -0800 |
---|---|---|
committer | Daniel Vassdal <shutter@canternet.org> | 2014-02-16 11:07:36 -0800 |
commit | a71d0f67576bff27882f7f1a0b0b48179a581090 (patch) | |
tree | 002943f610b96c0578d2f13f0e7f8728f930c67b | |
parent | 50d7bf710ec0501b7309892646a31da065aaae5a (diff) |
enc_bcrypt: Add module
-rw-r--r-- | modules/extra/enc_bcrypt.cpp | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/modules/extra/enc_bcrypt.cpp b/modules/extra/enc_bcrypt.cpp new file mode 100644 index 000000000..67f2e8bef --- /dev/null +++ b/modules/extra/enc_bcrypt.cpp @@ -0,0 +1,121 @@ +/* RequiredLibraries: xcrypt */ + +#include "module.h" +#include "encryption.h" +#include <xcrypt.h> + +class EBCRYPT : public Module +{ + unsigned int rounds; + + Anope::string Salt() + { + char entropy[16]; + for (unsigned int i = 0; i < sizeof(entropy); i++) + entropy[i] = static_cast<char>(rand() % 0xFF); + + char salt[32]; + if (!crypt_gensalt_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt))) + return ""; + return salt; + } + + Anope::string Generate(const Anope::string& data, const Anope::string& salt) + { + char hash[64]; + crypt_rn(data.c_str(), salt.c_str(), hash, sizeof(hash)); + return hash; + } + + bool Compare(const Anope::string& string, const Anope::string& hash) + { + Anope::string ret = Generate(string, hash); + if (ret.empty()) + return false; + + return (ret == hash); + } + + public: + EBCRYPT(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR | EXTRA), + rounds(10) + { + // Should obviously never happen, but if xcrypt somehow was compiled without support for $2a$ I guess it might + if (Salt() == "") + throw ModuleException("Could not salt! BCrypt will not be available!"); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override + { + dest = "bcrypt:" + Generate(src, Salt()); + Log(LOG_DEBUG_2) << "(enc_bcrypt) hashed password from [" << src << "] to [" << dest << "]"; + return EVENT_ALLOW; + } + + void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override + { + const NickAlias *na = NickAlias::Find(req->GetAccount()); + if (na == NULL) + return; + NickCore *nc = na->nc; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (hash_method != "bcrypt") + return; + + if (Compare(req->GetPassword(), nc->pass.substr(7))) + { + /* if we are NOT the first module in the list, + * we want to re-encrypt the pass with the new encryption + */ + + unsigned int hashrounds = 0; + try + { + size_t roundspos = nc->pass.find('$', 11); + if (roundspos == Anope::string::npos) + throw ConvertException("Could not find hashrounds"); + + hashrounds = convertTo<unsigned int>(nc->pass.substr(11, roundspos - 11)); + } + catch (const ConvertException &) + { + Log(this) << "Could not get the round size of a hash. This is probably a bug. Hash: " << nc->pass; + } + + if (ModuleManager::FindFirstOf(ENCRYPTION) != this || (hashrounds && hashrounds != rounds)) + Anope::Encrypt(req->GetPassword(), nc->pass); + req->Success(this); + } + } + + void OnReload(Configuration::Conf *conf) anope_override + { + Configuration::Block *block = conf->GetModule(this); + rounds = block->Get<unsigned int>("rounds", "10"); + + if (rounds == 0) + { + rounds = 10; + Log(this) << "Rounds can't be 0! Setting ignored."; + } + else if (rounds < 10) + { + Log(this) << "10 to 12 rounds is recommended."; + } + else if (rounds >= 32) + { + rounds = 10; + Log(this) << "The maximum number of rounds supported is 31. Ignoring setting and using 10."; + } + else if (rounds >= 14) + { + Log(this) << "Are you sure you want to use " << stringify(rounds) << " in your bcrypt settings? This is very CPU intensive! Recommended rounds is 10-12."; + } + } +}; + +MODULE_INIT(EBCRYPT) |