aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorGravatar Sadie Powell2023-04-27 11:00:42 +0100
committerGravatar Sadie Powell2023-04-27 11:00:42 +0100
commit3c056d489c39d90402fd8b40778ce91505d95cbf (patch)
tree2c52e50fdf9bd09ee61a69333ccbc13e75d6cd3f /src/modules
parentAllow using rmode on a prefix char and mode name. (diff)
parentWhen building the PACKAGE project we don't need to specify a target. (diff)
Merge branch 'insp3' into master.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/extra/m_ssl_gnutls.cpp23
-rw-r--r--src/modules/extra/m_ssl_mbedtls.cpp27
-rw-r--r--src/modules/extra/m_ssl_openssl.cpp32
-rw-r--r--src/modules/m_codepage.cpp97
-rw-r--r--src/modules/m_rmode.cpp2
-rw-r--r--src/modules/m_sslinfo.cpp16
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