diff options
author | Sadie Powell <sadie@witchery.services> | 2020-09-10 15:15:34 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-10 15:15:34 +0100 |
commit | 8180dd414ecd43758290c20315e70af72f0833bf (patch) | |
tree | 195ea018ed2d477a114b422caee94053419e77ef | |
parent | ff28985384cb6e60fc7479f93778c4363c322ea7 (diff) |
Implement support for immutable account identifiers.
-rw-r--r-- | include/account.h | 10 | ||||
-rw-r--r-- | include/anope.h | 7 | ||||
-rw-r--r-- | modules/protocol/inspircd3.cpp | 2 | ||||
-rw-r--r-- | src/init.cpp | 6 | ||||
-rw-r--r-- | src/nickcore.cpp | 55 | ||||
-rw-r--r-- | src/siphash.cpp | 125 |
6 files changed, 201 insertions, 4 deletions
diff --git a/include/account.h b/include/account.h index 8cb2e7a8e..a65c390ea 100644 --- a/include/account.h +++ b/include/account.h @@ -20,9 +20,11 @@ typedef Anope::hash_map<NickAlias *> nickalias_map; typedef Anope::hash_map<NickCore *> nickcore_map; +typedef TR1NS::unordered_map<uint64_t, NickCore *> nickcoreid_map; extern CoreExport Serialize::Checker<nickalias_map> NickAliasList; extern CoreExport Serialize::Checker<nickcore_map> NickCoreList; +extern CoreExport nickcoreid_map NickCoreIdList; /* A registered nickname. * It matters that Base is here before Extensible (it is inherited by Serializable) @@ -107,6 +109,8 @@ class CoreExport NickCore : public Serializable, public Extensible { /* Channels which reference this core in some way (this is on their access list, akick list, is founder, successor, etc) */ Serialize::Checker<std::map<ChannelInfo *, int> > chanaccess; + /* Unique identifier for the account. */ + uint64_t id; public: /* Name of the account. Find(display)->nc == this. */ Anope::string display; @@ -140,8 +144,9 @@ class CoreExport NickCore : public Serializable, public Extensible /** Constructor * @param display The display nick + * @param id The account id */ - NickCore(const Anope::string &nickdisplay); + NickCore(const Anope::string &nickdisplay, uint64_t nickid = 0); ~NickCore(); void Serialize(Serialize::Data &data) const anope_override; @@ -178,6 +183,9 @@ class CoreExport NickCore : public Serializable, public Extensible */ unsigned GetAccessCount() const; + /** Retrieves the account id for this user */ + uint64_t GetId(); + /** Find an entry in the nick's access list * * @param entry The nick!ident@host entry to search for diff --git a/include/anope.h b/include/anope.h index e53f5c3a6..d4a351a93 100644 --- a/include/anope.h +++ b/include/anope.h @@ -470,6 +470,13 @@ namespace Anope */ extern CoreExport bool Decrypt(const Anope::string &src, Anope::string &dest); + /** Hashes a buffer with SipHash-2-4 + * @param src The start of the buffer to hash + * @param src_sz The total number of bytes in the buffer + * @param key A 16 byte key to hash the buffer with. + */ + extern CoreExport uint64_t SipHash24(const void *src, unsigned long src_sz, const char key[16]); + /** Returns a sequence of data formatted as the format argument specifies. ** After the format parameter, the function expects at least as many ** additional arguments as specified in format. diff --git a/modules/protocol/inspircd3.cpp b/modules/protocol/inspircd3.cpp index ec97eb0e1..de9406258 100644 --- a/modules/protocol/inspircd3.cpp +++ b/modules/protocol/inspircd3.cpp @@ -406,11 +406,13 @@ class InspIRCd3Proto : public IRCDProto return; UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :" << na->nc->display; + UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountid :" << na->nc->GetId(); } void SendLogout(User *u) anope_override { UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :"; + UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountid :"; } void SendChannel(Channel *c) anope_override diff --git a/src/init.cpp b/src/init.cpp index d02b69293..31eae9901 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -551,6 +551,12 @@ void Anope::Init(int ac, char **av) EventReturn MOD_RESULT; FOREACH_RESULT(OnLoadDatabase, MOD_RESULT, ()); static_cast<void>(MOD_RESULT); + + /* Compatibility for databases without ids */ + for (nickcore_map::iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it) + it->second->GetId(); + /* End compatibility for databases without ids */ + Log() << "Databases loaded"; FOREACH_MOD(OnPostInit, ()); diff --git a/src/nickcore.cpp b/src/nickcore.cpp index b43a8b3c1..6b6a00138 100644 --- a/src/nickcore.cpp +++ b/src/nickcore.cpp @@ -13,10 +13,12 @@ #include "modules.h" #include "account.h" #include "config.h" +#include <climits> Serialize::Checker<nickcore_map> NickCoreList("NickCore"); +nickcoreid_map NickCoreIdList; -NickCore::NickCore(const Anope::string &coredisplay) : Serializable("NickCore"), chanaccess("ChannelInfo"), aliases("NickAlias") +NickCore::NickCore(const Anope::string &coredisplay, uint64_t coreid) : Serializable("NickCore"), chanaccess("ChannelInfo"), aliases("NickAlias") { if (coredisplay.empty()) throw CoreException("Empty display passed to NickCore constructor"); @@ -26,12 +28,16 @@ NickCore::NickCore(const Anope::string &coredisplay) : Serializable("NickCore"), this->lastmail = 0; this->display = coredisplay; + this->id = coreid; size_t old = NickCoreList->size(); (*NickCoreList)[this->display] = this; if (old == NickCoreList->size()) Log(LOG_DEBUG) << "Duplicate account " << coredisplay << " in nickcore table?"; + if (this->id) + NickCoreIdList[this->id] = this; + FOREACH_MOD(OnNickCoreCreate, (this)); } @@ -52,6 +58,8 @@ NickCore::~NickCore() this->users.clear(); NickCoreList->erase(this->display); + if (this->id) + NickCoreIdList.erase(this->id); this->ClearAccess(); @@ -66,6 +74,7 @@ NickCore::~NickCore() void NickCore::Serialize(Serialize::Data &data) const { data["display"] << this->display; + data["id"] << this->id; data["pass"] << this->pass; data["email"] << this->email; data["language"] << this->language; @@ -82,13 +91,15 @@ Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) NickCore *nc; Anope::string sdisplay; - data["display"] >> sdisplay; + uint64_t sid = 0; + data["id"] >> sid; + if (obj) nc = anope_dynamic_static_cast<NickCore *>(obj); else - nc = new NickCore(sdisplay); + nc = new NickCore(sdisplay, sid); data["pass"] >> nc->pass; data["email"] >> nc->email; @@ -267,3 +278,41 @@ NickCore* NickCore::Find(const Anope::string &nick) return NULL; } + +uint64_t NickCore::GetId() +{ + if (this->id) + return this->id; + + NickAlias *na = NickAlias::Find(this->display); + if (!na) + { + Log(LOG_DEBUG) << "Unable to find the display NickAlias for NickCore: " << this->display; + return 0; + } + + Anope::string secretid = this->display + "\0" + stringify(na->time_registered); + + // Generate the account id. This should almost always only have one + // iteration but in the rare case that we generate a duplicate id we try + // again with a new key. + while (!this->id) + { + // Generate a random key for SipHash. + char key[16]; + for (size_t i = 0; i < sizeof(key); ++i) + key[i] = rand() % CHAR_MAX; + + uint64_t newid = Anope::SipHash24(secretid.c_str(), secretid.length(), key); + nickcoreid_map::const_iterator it = NickCoreIdList.find(newid); + if (it == NickCoreIdList.end()) + { + this->id = newid; + NickCoreIdList[this->id] = this; + this->QueueUpdate(); + break; + } + } + + return this->id; +} diff --git a/src/siphash.cpp b/src/siphash.cpp new file mode 100644 index 000000000..abe6b53bf --- /dev/null +++ b/src/siphash.cpp @@ -0,0 +1,125 @@ +/* SipHash-2-4 routines. + * + * (C) 2003-2020 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +/* <MIT License> + Copyright (c) 2013 Marek Majkowski <marek@popcount.org> + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + </MIT License> + Original location: + https://github.com/majek/csiphash/ + Solution inspired by code from: + Samuel Neves (supercop/crypto_auth/siphash24/little) + djb (supercop/crypto_auth/siphash24/little2) + Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) +*/ + +#include "services.h" +#include "anope.h" + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define _le64toh(x) ((uint64_t)(x)) +#elif defined(_WIN32) +/* Windows is always little endian, unless you're on xbox360 + http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */ +# define _le64toh(x) ((uint64_t)(x)) +#elif defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define _le64toh(x) OSSwapLittleToHostInt64(x) +#else + +/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */ +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/endian.h> +# else +# include <endian.h> +# endif +# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN +# define _le64toh(x) ((uint64_t)(x)) +# else +# define _le64toh(x) le64toh(x) +# endif + +#endif + + +#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) + +#define HALF_ROUND(a,b,c,d,s,t) \ + a += b; c += d; \ + b = ROTATE(b, s) ^ a; \ + d = ROTATE(d, t) ^ c; \ + a = ROTATE(a, 32); + +#define DOUBLE_ROUND(v0,v1,v2,v3) \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + HALF_ROUND(v2,v1,v0,v3,17,21); \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + HALF_ROUND(v2,v1,v0,v3,17,21); + + +uint64_t Anope::SipHash24(const void *src, unsigned long src_sz, const char key[16]) +{ + const uint64_t *_key = (uint64_t *)key; + uint64_t k0 = _le64toh(_key[0]); + uint64_t k1 = _le64toh(_key[1]); + uint64_t b = (uint64_t)src_sz << 56; + const uint64_t *in = (uint64_t*)src; + + uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; + uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; + uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; + uint64_t v3 = k1 ^ 0x7465646279746573ULL; + + while (src_sz >= 8) + { + uint64_t mi = _le64toh(*in); + in += 1; src_sz -= 8; + v3 ^= mi; + DOUBLE_ROUND(v0,v1,v2,v3); + v0 ^= mi; + } + + uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in; + switch (src_sz) + { + case 7: pt[6] = m[6]; + case 6: pt[5] = m[5]; + case 5: pt[4] = m[4]; + case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break; + case 3: pt[2] = m[2]; + case 2: pt[1] = m[1]; + case 1: pt[0] = m[0]; + } + b |= _le64toh(t); + + v3 ^= b; + DOUBLE_ROUND(v0,v1,v2,v3); + v0 ^= b; v2 ^= 0xff; + DOUBLE_ROUND(v0,v1,v2,v3); + DOUBLE_ROUND(v0,v1,v2,v3); + return (v0 ^ v1) ^ (v2 ^ v3); +} |