/* RequiredLibraries: ssl,crypto */ #include "module.h" #include "modules/sasl.h" #include #include #include using namespace SASL; class DHBS : public Mechanism { void Err(Session* sess, BIGNUM* key = NULL) { if (key) BN_free(key); sasl->Fail(sess); delete sess; } public: struct DHBSSession : SASL::Session { DH* dh; DHBSSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u) { if (!(dh = DH_new())) return; dh->g = BN_dup(dh_params->g); dh->p = BN_dup(dh_params->p); if (!DH_generate_key(dh)) { DH_free(dh); dh = NULL; } } ~DHBSSession() { if (dh) DH_free(dh); } }; DH* dh_params; const size_t keysize; SASL::Session* CreateSession(const Anope::string &uid) anope_override { return new DHBSSession(this, uid, dh_params); } DHBS(Module *o) : Mechanism(o, "DH-BLOWFISH"), keysize(256 / 8) { if (!(dh_params = DH_new())) throw ModuleException("DH_new() failed!"); if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL)) { DH_free(dh_params); throw ModuleException("Could not generate DH-params"); } } ~DHBS() { DH_free(dh_params); } void ProcessMessage(SASL::Session *session, const SASL::Message &m) anope_override { DHBSSession *sess = anope_dynamic_static_cast(session); if (!sess->dh) { sasl->SendMessage(sess, "D", "A"); delete sess; return; } if (m.type == "S") { // Format: [ss]

[ss][ss] // Where ss is a unsigned short with the size of the key const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key }; // Find the size of our buffer - initialized at 6 because of string size data size_t size = 6; for (size_t i = 0; i < 3; i++) size += BN_num_bytes(dhval[i]); // Fill in the DH data std::vector buffer(size); for (size_t i = 0, pos = 0; i < 3; i++) { *reinterpret_cast(&buffer[pos]) = htons(BN_num_bytes(dhval[i])); pos += 2; BN_bn2bin(dhval[i], &buffer[pos]); pos += BN_num_bytes(dhval[i]); } Anope::string encoded; Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded); sasl->SendMessage(sess, "C", encoded); } else if (m.type == "C") { // Make sure we have some data - actual size check is done later if (m.data.length() < 10) return Err(sess); // Format: [ss]<\0> Anope::string decoded; Anope::B64Decode(m.data, decoded); // As we rely on the client giving us a null terminator at the right place, // let's add one extra in case the client tries to crash us const size_t decodedlen = decoded.length(); decoded.push_back('\0'); // Make sure we have enough data for at least the key, a one letter username, and a block of data if (decodedlen < keysize + 2 + 2 + 8) return Err(sess); const unsigned char* data = reinterpret_cast(decoded.data()); // Control the size of the key if (ntohs(*reinterpret_cast(&data[0])) != keysize) return Err(sess); // Convert pubkey from binary size_t pos = 2; BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL); if (!pubkey) return Err(sess); // Find shared key std::vector secretkey(DH_size(sess->dh) + 1, 0); if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast(keysize)) return Err(sess, pubkey); // Set decryption key BF_KEY BFKey; BF_set_key(&BFKey, keysize, &secretkey[0]); pos += keysize; const Anope::string username = reinterpret_cast(&data[pos]); // Check that the username is valid, and that we have at least one block of data // 2 + 1 + 8 = uint16_t size for keylen, \0 for username, 8 for one block of data if (username.empty() || username.length() + keysize + 2 + 1 + 8 > decodedlen) return Err(sess, pubkey); pos += username.length() + 1; size_t size = decodedlen - pos; // Blowfish data blocks are 64 bits wide - valid format? if (size % 8) return Err(sess, pubkey); std::vector decrypted(size + 1, 0); for (size_t i = 0; i < size; i += 8) BF_ecb_encrypt(&data[pos + i], reinterpret_cast(&decrypted[i]), &BFKey, BF_DECRYPT); std::string password = &decrypted[0]; if (password.empty()) return Err(sess, pubkey); SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password); FOREACH_MOD(OnCheckAuthentication, (NULL, req)); req->Dispatch(); BN_free(pubkey); } } }; class ModuleSASLDHBS : public Module { DHBS dhbs; public: ModuleSASLDHBS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA), dhbs(this) { } }; MODULE_INIT(ModuleSASLDHBS)