diff options
| author | 2023-04-27 11:00:42 +0100 | |
|---|---|---|
| committer | 2023-04-27 11:00:42 +0100 | |
| commit | 3c056d489c39d90402fd8b40778ce91505d95cbf (patch) | |
| tree | 2c52e50fdf9bd09ee61a69333ccbc13e75d6cd3f /src | |
| parent | Allow using rmode on a prefix char and mode name. (diff) | |
| parent | When building the PACKAGE project we don't need to specify a target. (diff) | |
Merge branch 'insp3' into master.
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/extra/m_ssl_gnutls.cpp | 23 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_mbedtls.cpp | 27 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_openssl.cpp | 32 | ||||
| -rw-r--r-- | src/modules/m_codepage.cpp | 97 | ||||
| -rw-r--r-- | src/modules/m_rmode.cpp | 2 | ||||
| -rw-r--r-- | src/modules/m_sslinfo.cpp | 16 |
6 files changed, 176 insertions, 21 deletions
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 4b26bbc03..63c4dd97a 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -727,11 +727,26 @@ private: certinfo->fingerprint = Hex::Encode(buffer, buffer_size); } - /* Beware here we do not check for errors. - */ - if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time())) + certinfo->activation = gnutls_x509_crt_get_activation_time(cert); + if (certinfo->activation == -1) + { + certinfo->activation = 0; + certinfo->error = "Unable to check certificate activation time"; + } + else if (certinfo->activation >= ServerInstance->Time()) + { + certinfo->error = "Certificate not activated"; + } + + certinfo->expiration = gnutls_x509_crt_get_expiration_time(cert); + if (certinfo->expiration == -1) + { + certinfo->expiration = 0; + certinfo->error = "Unable to check certificate expiration time"; + } + else if (certinfo->expiration <= ServerInstance->Time()) { - certinfo->error = "Not activated, or expired certificate"; + certinfo->error = "Certificate has expired"; } info_done_dealloc: diff --git a/src/modules/extra/m_ssl_mbedtls.cpp b/src/modules/extra/m_ssl_mbedtls.cpp index daf0318b3..e5700e7a2 100644 --- a/src/modules/extra/m_ssl_mbedtls.cpp +++ b/src/modules/extra/m_ssl_mbedtls.cpp @@ -29,6 +29,10 @@ #include "inspircd.h" #include "modules/ssl.h" +#ifdef _WIN32 +# define timegm _mkgmtime +#endif + #include <mbedtls/ctr_drbg.h> #include <mbedtls/dhm.h> #include <mbedtls/ecp.h> @@ -632,6 +636,8 @@ private: return; } + certificate->activation = GetTime(&cert->valid_from); + certificate->expiration = GetTime(&cert->valid_to); if (flags == 0) { // Verification succeeded @@ -641,8 +647,10 @@ private: { // Verification failed certificate->trusted = false; - if ((flags & MBEDTLS_X509_BADCERT_EXPIRED) || (flags & MBEDTLS_X509_BADCERT_FUTURE)) - certificate->error = "Not activated, or expired certificate"; + if (flags & MBEDTLS_X509_BADCERT_FUTURE) + certificate->error = "Certificate not activated"; + else if (flags & MBEDTLS_X509_BADCERT_EXPIRED) + certificate->error = "Certificate has expired"; } certificate->unknownsigner = (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED); @@ -665,6 +673,21 @@ private: out[pos] = ' '; } + static time_t GetTime(const mbedtls_x509_time* x509time) + { + // HACK: this is terrible but there's no sensible way I can see to get + // a time_t from this. + tm ts; + ts.tm_year = x509time->year - 1900; + ts.tm_mon = x509time->mon - 1; + ts.tm_mday = x509time->day; + ts.tm_hour = x509time->hour; + ts.tm_min = x509time->min; + ts.tm_sec = x509time->sec; + + return timegm(&ts); + } + static int Pull(void* userptr, unsigned char* buffer, size_t size) { StreamSocket* const sock = reinterpret_cast<StreamSocket*>(userptr); diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 648c8e724..65d300601 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -49,6 +49,7 @@ #include <openssl/dh.h> #ifdef _WIN32 +# define timegm _mkgmtime # pragma comment(lib, "libcrypto.lib") # pragma comment(lib, "libssl.lib") #endif @@ -618,10 +619,16 @@ private: certinfo->fingerprint = Hex::Encode(md, n); } - if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0)) - { - certinfo->error = "Not activated, or expired certificate"; - } + certinfo->activation = GetTime(X509_getm_notBefore(cert)); + certinfo->expiration = GetTime(X509_getm_notAfter(cert)); + + int activated = ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()); + if (activated != -1 && activated != 0) + certinfo->error = "Certificate not activated"; + + int expired = ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()); + if (expired != 0 && expired != 1) + certinfo->error = "Certificate has expired"; X509_free(cert); } @@ -636,6 +643,23 @@ private: out[pos] = ' '; } + static time_t GetTime(ASN1_TIME* x509time) + { +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + if (!x509time) + return 0; + + struct tm ts; + if (!ASN1_TIME_to_tm(x509time, &ts)) + return 0; + + return timegm(&ts); +#else + // OpenSSL 1.1 is required for ASN_TIME_to_tm. + return 0; +#endif + } + void SSLInfoCallback(int where, int rc) { if ((where & SSL_CB_HANDSHAKE_START) && (status == STATUS_OPEN)) diff --git a/src/modules/m_codepage.cpp b/src/modules/m_codepage.cpp index 675255238..583cd8c03 100644 --- a/src/modules/m_codepage.cpp +++ b/src/modules/m_codepage.cpp @@ -188,7 +188,7 @@ private: std::string charset; template <typename T> - void RehashHashmap(T& hashmap) + static void RehashHashmap(T& hashmap) { T newhash(hashmap.bucket_count()); for (const auto& [key, value] : hashmap) @@ -196,15 +196,90 @@ private: hashmap.swap(newhash); } + static void DestroyChannel(Channel* chan) + { + // Remove all of the users from the channel. Using KICK here will mean + // the user's client will probably attempt to rejoin and will enter the + // succeeding channel. Unfortunately this is the best we can do for now. + while (!chan->userlist.empty()) + chan->KickUser(ServerInstance->FakeClient, chan->userlist.begin(), "This channel does not exist anymore."); + + // Remove all modes from the channel just in case one of them keeps the channel open. + Modes::ChangeList changelist; + for (const auto& [_, mh] : ServerInstance->Modes.GetModes(MODETYPE_CHANNEL)) + mh->RemoveMode(chan, changelist); + ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY); + + // The channel will be destroyed automatically by CheckDestroy. + } + + static void ChangeNick(User* user, const std::string& message) + { + user->WriteNumeric(RPL_SAVENICK, user->uuid, message); + user->ChangeNick(user->uuid); + } + + static void CheckDuplicateChan() + { + ChannelMap duplicates; + for (auto &[_, chan] : ServerInstance->Channels.GetChans()) + { + auto check = duplicates.insert(std::make_pair(chan->name, chan)); + if (check.second) + continue; // No duplicate. + + Channel* otherchan = check.first->second; + if (otherchan->age < chan->age) + { + // The other channel was created first. + DestroyChannel(chan); + } + else if (otherchan->age > chan->age) + { + // The other channel was created last. + DestroyChannel(otherchan); + check.first->second = chan; + } + else + { + // Both created at the same time. + DestroyChannel(chan); + DestroyChannel(otherchan); + duplicates.erase(check.first); + } + } + } + static void CheckDuplicateNick() { - insp::flat_set<std::string, irc::insensitive_swo> duplicates; + UserMap duplicates; for (auto* user : ServerInstance->Users.GetLocalUsers()) { - if (user->nick != user->uuid && !duplicates.insert(user->nick).second) + if (user->nick == user->uuid) + continue; // UUID users are always unique. + + auto check = duplicates.insert(std::make_pair(user->nick, user)); + if (check.second) + continue; // No duplicate. + + User* otheruser = check.first->second; + if (otheruser->nickchanged < user->nickchanged) { - user->WriteNumeric(RPL_SAVENICK, user->uuid, "Your nickname is no longer available."); - user->ChangeNick(user->uuid); + // The other user connected first. + ChangeNick(user, "Your nickname is no longer available."); + } + else if (otheruser->nickchanged > user->nickchanged) + { + // The other user connected last. + ChangeNick(otheruser, "Your nickname is no longer available."); + check.first->second = user; + } + else + { + // Both connected at the same time. + ChangeNick(user, "Your nickname is no longer available."); + ChangeNick(otheruser, "Your nickname is no longer available."); + duplicates.erase(check.first); } } } @@ -214,14 +289,11 @@ private: for (auto* user : ServerInstance->Users.GetLocalUsers()) { if (user->nick != user->uuid && !ServerInstance->IsNick(user->nick)) - { - user->WriteNumeric(RPL_SAVENICK, user->uuid, "Your nickname is no longer valid."); - user->ChangeNick(user->uuid); - } + ChangeNick(user, "Your nickname is no longer valid."); } } - void CheckRehash(unsigned char* prevmap) + static void CheckRehash(unsigned char* prevmap) { if (!memcmp(prevmap, national_case_insensitive_map, UCHAR_MAX)) return; @@ -248,6 +320,7 @@ public: ServerInstance->Config->CaseMapping = origcasemapname; national_case_insensitive_map = origcasemap; + CheckDuplicateChan(); CheckDuplicateNick(); if (codepage) // nullptr if ReadConfig throws on load. CheckRehash(codepage->casemap); @@ -319,7 +392,11 @@ public: ServerInstance->Config->CaseMapping = name; national_case_insensitive_map = codepage->casemap; if (newcodepage) // nullptr on first read. + { + CheckDuplicateChan(); + CheckDuplicateNick(); CheckRehash(newcodepage->casemap); + } } void OnBuildISupport(ISupport::TokenMap& tokens) override diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp index 04097346e..4335330b9 100644 --- a/src/modules/m_rmode.cpp +++ b/src/modules/m_rmode.cpp @@ -60,7 +60,7 @@ public: ModeHandler* mh = FindMode(parameters[1]); if (!mh) { - user->WriteNumeric(ERR_UNKNOWNMODE, parameters[0], "is not a recognised channel mode."); + user->WriteNumeric(ERR_UNKNOWNMODE, parameters[1], "is not a recognised channel mode."); return CmdResult::FAILURE; } diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp index 046481754..7e21c454b 100644 --- a/src/modules/m_sslinfo.cpp +++ b/src/modules/m_sslinfo.cpp @@ -27,6 +27,7 @@ #include "inspircd.h" +#include "duration.h" #include "extension.h" #include "modules/ssl.h" #include "modules/webirc.h" @@ -300,6 +301,7 @@ class ModuleSSLInfo final private: CommandSSLInfo cmd; std::string hash; + unsigned long warnexpiring; static bool MatchFP(ssl_cert* const cert, const std::string& fp) { @@ -322,6 +324,7 @@ public: cmd.operonlyfp = tag->getBool("operonly"); cmd.sslapi.localsecure = tag->getBool("localsecure", true); hash = tag->getString("hash"); + warnexpiring = tag->getDuration("warnexpiring", 0, 0, 60*60*24*365); } void OnWhois(Whois::Context& whois) override @@ -398,6 +401,19 @@ public: if (cert && !cert->GetFingerprint().empty()) text.append(" and your TLS client certificate fingerprint is ").append(cert->GetFingerprint()); user->WriteNotice(text); + + if (!cert || !warnexpiring || !cert->GetExpirationTime()) + return; + + if (ServerInstance->Time() > cert->GetExpirationTime()) + { + user->WriteNotice("*** Your TLS (SSL) client certificate has expired."); + } + else if (static_cast<time_t>(ServerInstance->Time() + warnexpiring) > cert->GetExpirationTime()) + { + const std::string duration = Duration::ToString(cert->GetExpirationTime() - ServerInstance->Time()); + user->WriteNotice("*** Your TLS (SSL) client certificate expires in " + duration + "."); + } } ModResult OnPreChangeConnectClass(LocalUser* user, const std::shared_ptr<ConnectClass>& klass, std::optional<Numeric::Numeric>& errnum) override |
