mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-09 10:39:02 -04:00
Merge branch 'insp3' into master.
This commit is contained in:
commit
3c056d489c
2
.github/workflows/ci-windows.yml
vendored
2
.github/workflows/ci-windows.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.1.3
|
||||
uses: microsoft/setup-msbuild@v1.3.1
|
||||
|
||||
- name: Setup Conan
|
||||
uses: turtlebrowser/get-conan@v1.2
|
||||
|
@ -2343,13 +2343,15 @@
|
||||
#
|
||||
# If you want to prevent users from viewing TLS certificate information
|
||||
# and fingerprints of other users, set operonly to yes. You can also set hash
|
||||
# to an IANA Hash Function Textual Name to use the TLS fingerprint sent by a
|
||||
# WebIRC gateway (requires the gateway module) and localsecure to allow
|
||||
# locally-connected connections where TLS is not necessary to be considered
|
||||
# secure.
|
||||
# to an IANA Hash Function Textual Name to use the SSL fingerprint sent by a
|
||||
# WebIRC gateway (requires the cgiirc module), localsecure to allow locally
|
||||
# connected connections where TLS is not necessary to be considered secure,
|
||||
# and warnexpiring to warn users when their client certificate is about to
|
||||
# expire.
|
||||
#<sslinfo operonly="no"
|
||||
# hash="sha-256"
|
||||
# localsecure="yes">
|
||||
# localsecure="yes"
|
||||
# warnexpiring="1w">
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# mbedTLS TLS module: Adds support for TLS connections using mbedTLS.
|
||||
|
@ -50,6 +50,9 @@ public:
|
||||
bool invalid = true;
|
||||
bool unknownsigner = true;
|
||||
bool revoked = false;
|
||||
time_t activation = 0;
|
||||
time_t expiration = 0;
|
||||
|
||||
|
||||
/** Get certificate distinguished name
|
||||
* @return Certificate DN
|
||||
@ -137,6 +140,22 @@ public:
|
||||
{
|
||||
return IsUsable() && trusted && !unknownsigner;
|
||||
}
|
||||
|
||||
/** Retrieves the client certificate activation time.
|
||||
* @param The time the client certificate was activated or 0 on error.
|
||||
*/
|
||||
time_t GetActivationTime() const
|
||||
{
|
||||
return activation;
|
||||
}
|
||||
|
||||
/** Retrieves the client certificate expiration time.
|
||||
* @param The time the client certificate will expire or 0 on error.
|
||||
*/
|
||||
time_t GetExpirationTime() const
|
||||
{
|
||||
return expiration;
|
||||
}
|
||||
};
|
||||
|
||||
/** I/O hook provider for TLS modules. */
|
||||
|
@ -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->error = "Not activated, or expired certificate";
|
||||
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 = "Certificate has expired";
|
||||
}
|
||||
|
||||
info_done_dealloc:
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
4
win/build.ps1
Normal file
4
win/build.ps1
Normal file
@ -0,0 +1,4 @@
|
||||
cd $PSScriptRoot/build
|
||||
conan install --build=missing ..
|
||||
cmake .. -A x64 -D CMAKE_BUILD_TYPE=Release
|
||||
msbuild PACKAGE.vcxproj /P:Configuration=Release /P:Platform=x64 /VERBOSITY:MINIMAL
|
@ -1,4 +1,4 @@
|
||||
# Last updated: 2022-02-28
|
||||
# Last updated: 2022-04-22
|
||||
#
|
||||
# Modules we can't legally ship: geo_maxmind, ssl_mbedtls, ssl_openssl
|
||||
# Modules which don't apply to Windows: sslrehashsignal
|
||||
@ -8,14 +8,14 @@
|
||||
argon2/20190702
|
||||
## libmaxminddb/1.7.1
|
||||
libmysqlclient/8.0.31
|
||||
libpq/14.5
|
||||
libpq/14.7
|
||||
libpsl/0.21.1
|
||||
## mbedtls/3.2.1
|
||||
## openssl/1.1.1t # unable to upgrade to v3 yet because of dependency issues
|
||||
pcre2/10.42
|
||||
rapidjson/cci.20220822
|
||||
re2/20230201
|
||||
sqlite3/3.40.1
|
||||
re2/20230301
|
||||
sqlite3/3.41.1
|
||||
|
||||
[options]
|
||||
argon2:shared=True
|
||||
|
Loading…
x
Reference in New Issue
Block a user