From f91a61fa22b239384c31526fd11da1e3030aaa96 Mon Sep 17 00:00:00 2001 From: danieldg Date: Sun, 21 Feb 2010 17:08:45 +0000 Subject: Add Base64 encode/decode functions to the core git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@12507 e03df62e-2008-0410-955e-edbf42e46eb7 --- include/inspstring.h | 4 + src/inspstring.cpp | 72 ++++++++ src/modules/extra/m_ziplink.cpp | 370 ---------------------------------------- src/modules/m_hash.h | 9 +- src/modules/m_httpd_acl.cpp | 57 +------ src/modules/m_password_hash.cpp | 57 ++++++- 6 files changed, 139 insertions(+), 430 deletions(-) delete mode 100644 src/modules/extra/m_ziplink.cpp diff --git a/include/inspstring.h b/include/inspstring.h index dc7e00152..b11739999 100644 --- a/include/inspstring.h +++ b/include/inspstring.h @@ -41,6 +41,10 @@ CoreExport bool charremove(char* mp, char remove); /** Binary to hexadecimal conversion */ CoreExport std::string BinToHex(const std::string& data); +/** Base64 encode */ +CoreExport std::string BinToBase64(const std::string& data, const char* table = NULL, char pad = 0); +/** Base64 decode */ +CoreExport std::string Base64ToBin(const std::string& data, const char* table = NULL); #endif diff --git a/src/inspstring.cpp b/src/inspstring.cpp index 816e37a19..74629bf55 100644 --- a/src/inspstring.cpp +++ b/src/inspstring.cpp @@ -152,3 +152,75 @@ std::string BinToHex(const std::string& data) } return rv; } + +static const char b64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +std::string BinToBase64(const std::string& data_str, const char* table, char pad) +{ + if (!table) + table = b64table; + + uint32_t buffer; + uint8_t* data = (uint8_t*)data_str.data(); + std::string rv; + size_t i = 0; + while (i + 2 < data_str.length()) + { + buffer = (data[i] << 16 | data[i+1] << 8 | data[i+2]); + rv.push_back(table[0x3F & (buffer >> 18)]); + rv.push_back(table[0x3F & (buffer >> 12)]); + rv.push_back(table[0x3F & (buffer >> 6)]); + rv.push_back(table[0x3F & (buffer >> 0)]); + i += 3; + } + if (data_str.length() == i) + { + // no extra characters + } + else if (data_str.length() == i + 1) + { + buffer = data[i] << 16; + rv.push_back(table[0x3F & (buffer >> 18)]); + rv.push_back(table[0x3F & (buffer >> 12)]); + if (pad) + { + rv.push_back(pad); + rv.push_back(pad); + } + } + else if (data_str.length() == i + 2) + { + buffer = (data[i] << 16 | data[i] << 8); + rv.push_back(table[0x3F & (buffer >> 18)]); + rv.push_back(table[0x3F & (buffer >> 12)]); + rv.push_back(table[0x3F & (buffer >> 6)]); + if (pad) + rv.push_back(pad); + } + return rv; +} + +std::string Base64ToBin(const std::string& data_str, const char* table) +{ + if (!table) + table = b64table; + + bool ok = true; + int bitcount = 0; + uint32_t buffer; + const char* data = data_str.c_str(); + std::string rv; + while (ok) + { + const char* find = strchr(table, *data); + ok = (find && find < table + 64); + buffer = (buffer << 6) | (ok ? find - table : 0); + bitcount += 6; + if (bitcount >= 8) + { + bitcount -= 8; + rv.push_back((buffer >> bitcount) & 0xFF); + } + } + return rv; +} diff --git a/src/modules/extra/m_ziplink.cpp b/src/modules/extra/m_ziplink.cpp deleted file mode 100644 index 391ba7583..000000000 --- a/src/modules/extra/m_ziplink.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ - * - * InspIRCd: (C) 2002-2010 InspIRCd Development Team - * See: http://wiki.inspircd.org/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - * - * --------------------------------------------------- - */ - -#include "inspircd.h" -#include -#include - -/* $ModDesc: Provides zlib link support for servers */ -/* $LinkerFlags: -lz */ - -/* - * ZLIB_BEST_COMPRESSION (9) is used for all sending of data with - * a flush after each chunk. A frame may contain multiple lines - * and should be treated as raw binary data. - */ - -/* Status of a connection */ -enum izip_status { IZIP_CLOSED = 0, IZIP_OPEN }; - -/** Represents an zipped connections extra data - */ -class izip_session -{ - public: - z_stream c_stream; /* compression stream */ - z_stream d_stream; /* uncompress stream */ - izip_status status; /* Connection status */ - std::string outbuf; /* Holds output buffer (compressed) */ - std::string inbuf; /* Holds input buffer (compressed) */ -}; - -class ModuleZLib : public Module -{ - izip_session* sessions; - - /* Used for stats z extensions */ - float total_out_compressed; - float total_in_compressed; - float total_out_uncompressed; - float total_in_uncompressed; - - /* Used for reading data from the wire and compressing data to. */ - char *net_buffer; - unsigned int net_buffer_size; - public: - - ModuleZLib() - { - sessions = new izip_session[ServerInstance->SE->GetMaxFds()]; - for (int i = 0; i < ServerInstance->SE->GetMaxFds(); i++) - sessions[i].status = IZIP_CLOSED; - - total_out_compressed = total_in_compressed = 0; - total_out_uncompressed = total_in_uncompressed = 0; - Implementation eventlist[] = { I_OnStats }; - ServerInstance->Modules->Attach(eventlist, this, 1); - - // Allocate a buffer which is used for reading and writing data - net_buffer_size = ServerInstance->Config->NetBufferSize; - net_buffer = new char[net_buffer_size]; - } - - ~ModuleZLib() - { - delete[] sessions; - delete[] net_buffer; - } - - Version GetVersion() - { - return Version("Provides zlib link support for servers", VF_VENDOR); - } - - /* Handle stats z (misc stats) */ - ModResult OnStats(char symbol, User* user, string_list &results) - { - if (symbol == 'z') - { - std::string sn = ServerInstance->Config->ServerName; - - /* Yeah yeah, i know, floats are ew. - * We used them here because we'd be casting to float anyway to do this maths, - * and also only floating point numbers can deal with the pretty large numbers - * involved in the total throughput of a server over a large period of time. - * (we dont count 64 bit ints because not all systems have 64 bit ints, and floats - * can still hold more. - */ - float outbound_r = (total_out_compressed / (total_out_uncompressed + 0.001)) * 100; - float inbound_r = (total_in_compressed / (total_in_uncompressed + 0.001)) * 100; - - float total_compressed = total_in_compressed + total_out_compressed; - float total_uncompressed = total_in_uncompressed + total_out_uncompressed; - - float total_r = (total_compressed / (total_uncompressed + 0.001)) * 100; - - char outbound_ratio[MAXBUF], inbound_ratio[MAXBUF], combined_ratio[MAXBUF]; - - sprintf(outbound_ratio, "%3.2f%%", outbound_r); - sprintf(inbound_ratio, "%3.2f%%", inbound_r); - sprintf(combined_ratio, "%3.2f%%", total_r); - - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_compressed = "+ConvToStr(total_out_compressed)); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_compressed = "+ConvToStr(total_in_compressed)); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS outbound_uncompressed = "+ConvToStr(total_out_uncompressed)); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS inbound_uncompressed = "+ConvToStr(total_in_uncompressed)); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS percentage_of_original_outbound_traffic = "+outbound_ratio); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS percentage_of_orignal_inbound_traffic = "+inbound_ratio); - results.push_back(sn+" 304 "+user->nick+" :ZIPSTATS total_size_of_original_traffic = "+combined_ratio); - return MOD_RES_PASSTHRU; - } - - return MOD_RES_PASSTHRU; - } - - void OnStreamSocketConnect(StreamSocket* user) - { - OnStreamSocketAccept(user, 0, 0); - } - - void OnRawSocketAccept(StreamSocket* user, irc::sockets::sockaddrs*, irc::sockets::sockaddrs*) - { - int fd = user->GetFd(); - - izip_session* session = &sessions[fd]; - - /* Just in case... */ - session->outbuf.clear(); - - session->c_stream.zalloc = (alloc_func)0; - session->c_stream.zfree = (free_func)0; - session->c_stream.opaque = (voidpf)0; - - session->d_stream.zalloc = (alloc_func)0; - session->d_stream.zfree = (free_func)0; - session->d_stream.opaque = (voidpf)0; - - /* If we cant call this, well, we're boned. */ - if (inflateInit(&session->d_stream) != Z_OK) - { - session->status = IZIP_CLOSED; - return; - } - - /* Same here */ - if (deflateInit(&session->c_stream, Z_BEST_COMPRESSION) != Z_OK) - { - inflateEnd(&session->d_stream); - session->status = IZIP_CLOSED; - return; - } - - /* Just in case, do this last */ - session->status = IZIP_OPEN; - } - - void OnStreamSocketClose(StreamSocket* user) - { - int fd = user->GetFd(); - CloseSession(&sessions[fd]); - } - - int OnStreamSocketRead(StreamSocket* user, std::string& recvq) - { - int fd = user->GetFd(); - /* Find the sockets session */ - izip_session* session = &sessions[fd]; - - if (session->status == IZIP_CLOSED) - return -1; - - if (session->inbuf.empty()) - { - /* Read read_buffer_size bytes at a time to the buffer (usually 2.5k) */ - int readresult = read(fd, net_buffer, net_buffer_size); - - if (readresult < 0) - { - if (errno == EINTR || errno == EAGAIN) - return 0; - } - if (readresult <= 0) - return -1; - - total_in_compressed += readresult; - - /* Copy the compressed data into our input buffer */ - session->inbuf.append(net_buffer, readresult); - } - - size_t in_len = session->inbuf.length(); - char* buffer = ServerInstance->GetReadBuffer(); - int count = ServerInstance->Config->NetBufferSize; - - /* Prepare decompression */ - session->d_stream.next_in = (Bytef *)session->inbuf.c_str(); - session->d_stream.avail_in = in_len; - - session->d_stream.next_out = (Bytef*)buffer; - /* Last byte is reserved for NULL terminating that beast */ - session->d_stream.avail_out = count - 1; - - /* Z_SYNC_FLUSH: Do as much as possible */ - int ret = inflate(&session->d_stream, Z_SYNC_FLUSH); - /* TODO CloseStream() in here at random places */ - switch (ret) - { - case Z_NEED_DICT: - case Z_STREAM_ERROR: - /* This is one of the 'not supposed to happen' things. - * Memory corruption, anyone? - */ - Error(session, "General Error. This is not supposed to happen :/"); - break; - case Z_DATA_ERROR: - Error(session, "Decompression failed, malformed data"); - break; - case Z_MEM_ERROR: - Error(session, "Out of memory"); - break; - case Z_BUF_ERROR: - /* This one is non-fatal, buffer is just full - * (can't happen here). - */ - Error(session, "Internal error. This is not supposed to happen."); - break; - case Z_STREAM_END: - /* This module *never* generates these :/ */ - Error(session, "End-of-stream marker received"); - break; - case Z_OK: - break; - default: - /* NO WAI! This can't happen. All errors are handled above. */ - Error(session, "Unknown error"); - break; - } - if (ret != Z_OK) - { - return -1; - } - - /* Update the inbut buffer */ - unsigned int input_compressed = in_len - session->d_stream.avail_in; - session->inbuf = session->inbuf.substr(input_compressed); - - /* Update counters (Old size - new size) */ - unsigned int uncompressed_length = (count - 1) - session->d_stream.avail_out; - total_in_uncompressed += uncompressed_length; - - /* Null-terminate the buffer -- this doesnt harm binary data */ - recvq.append(buffer, uncompressed_length); - return 1; - } - - int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) - { - int fd = user->GetFd(); - izip_session* session = &sessions[fd]; - - if(session->status != IZIP_OPEN) - /* Seriously, wtf? */ - return -1; - - int ret; - - /* This loop is really only supposed to run once, but in case 'compr' - * is filled up somehow we are prepared to handle this situation. - */ - unsigned int offset = 0; - do - { - /* Prepare compression */ - session->c_stream.next_in = (Bytef*)sendq.data() + offset; - session->c_stream.avail_in = sendq.length() - offset; - - session->c_stream.next_out = (Bytef*)net_buffer; - session->c_stream.avail_out = net_buffer_size; - - /* Compress the text */ - ret = deflate(&session->c_stream, Z_SYNC_FLUSH); - /* TODO CloseStream() in here at random places */ - switch (ret) - { - case Z_OK: - break; - case Z_BUF_ERROR: - /* This one is non-fatal, buffer is just full - * (can't happen here). - */ - Error(session, "Internal error. This is not supposed to happen."); - break; - case Z_STREAM_ERROR: - /* This is one of the 'not supposed to happen' things. - * Memory corruption, anyone? - */ - Error(session, "General Error. This is also not supposed to happen."); - break; - default: - Error(session, "Unknown error"); - break; - } - - if (ret != Z_OK) - return 0; - - /* Space before - space after stuff was added to this */ - unsigned int compressed = net_buffer_size - session->c_stream.avail_out; - unsigned int uncompressed = sendq.length() - session->c_stream.avail_in; - - /* Make it skip the data which was compressed already */ - offset += uncompressed; - - /* Update stats */ - total_out_uncompressed += uncompressed; - total_out_compressed += compressed; - - /* Add compressed to the output buffer */ - session->outbuf.append((const char*)net_buffer, compressed); - } while (session->c_stream.avail_in != 0); - - /* Lets see how much we can send out */ - ret = write(fd, session->outbuf.data(), session->outbuf.length()); - - /* Check for errors, and advance the buffer if any was sent */ - if (ret > 0) - session->outbuf = session->outbuf.substr(ret); - else if (ret < 1) - { - if (errno == EAGAIN) - return 0; - else - { - session->outbuf.clear(); - return -1; - } - } - - return 1; - } - - void Error(izip_session* session, const std::string &text) - { - ServerInstance->SNO->WriteToSnoMask('l', "ziplink error: " + text); - } - - void CloseSession(izip_session* session) - { - if (session->status == IZIP_OPEN) - { - session->status = IZIP_CLOSED; - session->outbuf.clear(); - inflateEnd(&session->d_stream); - deflateEnd(&session->c_stream); - } - } - -}; - -MODULE_INIT(ModuleZLib) - diff --git a/src/modules/m_hash.h b/src/modules/m_hash.h index 7deb4c68c..edc9688b8 100644 --- a/src/modules/m_hash.h +++ b/src/modules/m_hash.h @@ -29,18 +29,21 @@ class HashProvider : public DataProvider return BinToHex(sum(data)); } + inline std::string b64sum(const std::string& data) + { + return BinToBase64(sum(data), NULL, 0); + } + /** Allows the IVs for the hash to be specified. As the choice of initial IV is * important for the security of a hash, this should not be used except to * maintain backwards compatability. This also allows you to change the hex * sequence from its default of "0123456789abcdef", which does not improve the * strength of the output, but helps confuse those attempting to implement it. * - * Only m_md5 implements this request; only m_cloaking should use it. - * * Example: * \code * unsigned int iv[] = { 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0xCCCCCCCC }; - * std::string result = Hash.sumIV(iv, "0123456789abcdef", "data"); + * std::string result = Hash.sumIV(iv, "fedcba9876543210", "data"); * \endcode */ virtual std::string sumIV(unsigned int* IV, const char* HexMap, const std::string &sdata) = 0; diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp index 94cc4045f..86d06fd25 100644 --- a/src/modules/m_httpd_acl.cpp +++ b/src/modules/m_httpd_acl.cpp @@ -105,61 +105,6 @@ class ModuleHTTPAccessList : public Module response.Send(); } - bool IsBase64(unsigned char c) - { - return (isalnum(c) || (c == '+') || (c == '/')); - } - - std::string Base64Decode(const std::string &base64) - { - const std::string base64_chars("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); - int inputlen = base64.length(); - int i = 0, j = 0, input = 0; - unsigned char longbuf[4], shortbuf[3]; - std::string retval; - - if (inputlen == 0) - return ""; - - while (inputlen-- && (base64[input] != '=') && IsBase64(base64[input])) - { - longbuf[i++] = base64[input]; - input++; - if (i == 4) - { - for (i = 0; i < 4; ++i) - longbuf[i] = base64_chars.find(longbuf[i]); - - shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4); - shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2); - shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3]; - - for (i = 0; i < 3; ++i) - retval += shortbuf[i]; - - i = 0; - } - } - - if (i) - { - for (j = i; j < 4; ++j) - longbuf[j] = 0; - - for (j = 0; j < 4; ++j) - longbuf[j] = base64_chars.find(longbuf[j]); - - shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4); - shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2); - shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3]; - - for (j = 0; j < i - 1; ++j) - retval += shortbuf[j]; - } - - return retval; - } - void OnEvent(Event& event) { if (event.id == "httpd_acl") @@ -230,7 +175,7 @@ class ModuleHTTPAccessList : public Module std::string pass; sep.GetToken(base64); - std::string userpass = Base64Decode(base64); + std::string userpass = Base64ToBin(base64); ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str()); irc::sepstream userpasspair(userpass, ':'); diff --git a/src/modules/m_password_hash.cpp b/src/modules/m_password_hash.cpp index a9870c057..d27856b3e 100644 --- a/src/modules/m_password_hash.cpp +++ b/src/modules/m_password_hash.cpp @@ -16,6 +16,19 @@ #include "inspircd.h" #include "m_hash.h" +static std::string hmac(HashProvider* hp, const std::string& key, const std::string& msg) +{ + std::string hmac1, hmac2; + for (size_t n = 0; n < key.length(); n++) + { + hmac1.push_back(static_cast(key[n] ^ 0x5C)); + hmac2.push_back(static_cast(key[n] ^ 0x36)); + } + hmac2.append(msg); + hmac1.append(hp->sum(hmac2)); + return hp->sum(hmac1); +} + /* Handle /MKPASSWD */ class CommandMkpasswd : public Command @@ -29,6 +42,22 @@ class CommandMkpasswd : public Command void MakeHash(User* user, const std::string& algo, const std::string& stuff) { + if (algo.substr(0,5) == "hmac-") + { + std::string type = algo.substr(5); + HashProvider* hp = ServerInstance->Modules->FindDataService("hash/" + type); + if (!hp) + { + user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str()); + return; + } + std::string salt = GenRandomStr(6, false); + std::string target = hmac(hp, salt, stuff); + std::string str = BinToBase64(salt) + "$" + BinToBase64(target, NULL, 0); + + user->WriteServ("NOTICE %s :%s hashed password for %s is %s", + user->nick.c_str(), algo.c_str(), stuff.c_str(), str.c_str()); + } HashProvider* hp = ServerInstance->Modules->FindDataService("hash/" + algo); if (hp) { @@ -38,7 +67,6 @@ class CommandMkpasswd : public Command } else { - /* I dont do flying, bob. */ user->WriteServ("NOTICE %s :Unknown hash type", user->nick.c_str()); } } @@ -68,6 +96,33 @@ class ModuleOperHash : public Module virtual ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) { + if (hashtype.substr(0,5) == "hmac-") + { + std::string type = hashtype.substr(5); + HashProvider* hp = ServerInstance->Modules->FindDataService("hash/" + type); + if (!hp) + return MOD_RES_PASSTHRU; + // this is a valid hash, from here on we either accept or deny + std::string::size_type sep = data.find('$'); + if (sep == std::string::npos) + return MOD_RES_DENY; + std::string salt = Base64ToBin(data.substr(0, sep)); + std::string target = Base64ToBin(data.substr(sep + 1)); + + std::string hmac1, hmac2; + for (size_t n = 0; n < salt.length(); n++) + { + hmac1.push_back(static_cast(salt[n] ^ 0x5C)); + hmac2.push_back(static_cast(salt[n] ^ 0x36)); + } + hmac2.append(input); + hmac1.append(hp->sum(hmac2)); + if (target == hp->sum(hmac1)) + return MOD_RES_ALLOW; + else + return MOD_RES_DENY; + } + HashProvider* hp = ServerInstance->Modules->FindDataService("hash/" + hashtype); /* Is this a valid hash name? */ -- cgit v1.2.3