diff options
| author | 2024-07-17 00:06:50 +0100 | |
|---|---|---|
| committer | 2024-07-17 00:06:50 +0100 | |
| commit | 889d521e05f2e922683d48c74eb00290e7a2f548 (patch) | |
| tree | b4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /modules/sslmodes.cpp | |
| parent | Use std::endian from C++20 in the sha1 module. (diff) | |
Shuffle the modules about a bit.
Diffstat (limited to 'modules/sslmodes.cpp')
| -rw-r--r-- | modules/sslmodes.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/modules/sslmodes.cpp b/modules/sslmodes.cpp new file mode 100644 index 000000000..07880eef1 --- /dev/null +++ b/modules/sslmodes.cpp @@ -0,0 +1,253 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013, 2017-2024 Sadie Powell <sadie@witchery.services> + * Copyright (C) 2013 Shawn Smith <ShawnSmith0828@gmail.com> + * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com> + * Copyright (C) 2012 Robby <robby@chatbelgie.be> + * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2009 Uli Schlachter <psychon@znc.in> + * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> + * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * Copyright (C) 2006-2007 Craig Edwards <brain@inspircd.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/callerid.h" +#include "modules/ctctags.h" +#include "modules/extban.h" +#include "modules/ssl.h" +#include "numerichelper.h" + +enum +{ + // From UnrealIRCd. + ERR_SECUREONLYCHAN = 489, + + // InspIRCd-specific. + ERR_ALLMUSTSSL = 490 +}; + +class FingerprintExtBan final + : public ExtBan::MatchingBase +{ +private: + UserCertificateAPI& sslapi; + +public: + FingerprintExtBan(Module* Creator, UserCertificateAPI& api) + : ExtBan::MatchingBase(Creator, "fingerprint", 'z') + , sslapi(api) + { + } + + bool IsMatch(User* user, Channel* channel, const std::string& text) override + { + if (!sslapi) + return false; + + for (const auto& fingerprint : sslapi->GetFingerprints(user)) + { + if (InspIRCd::Match(fingerprint, text)) + return true; + } + return false; + } +}; + +/** Handle channel mode +z + */ +class SSLMode final + : public ModeHandler +{ +private: + UserCertificateAPI& API; + +public: + SSLMode(Module* Creator, UserCertificateAPI& api) + : ModeHandler(Creator, "sslonly", 'z', PARAM_NONE, MODETYPE_CHANNEL) + , API(api) + { + } + + bool OnModeChange(User* source, User* dest, Channel* channel, Modes::Change& change) override + { + if (change.adding) + { + if (!channel->IsModeSet(this)) + { + if (IS_LOCAL(source)) + { + if (!API) + { + source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, "Unable to determine whether all members of the channel are connected via TLS"); + return false; + } + + size_t nonssl = 0; + for (const auto& [u, _] : channel->GetUsers()) + { + if (!API->IsSecure(u) && !u->server->IsService()) + nonssl++; + } + + if (nonssl) + { + source->WriteNumeric(ERR_ALLMUSTSSL, channel->name, fmt::format("All members of the channel must be connected via TLS ({}/{} are non-TLS)", + nonssl, channel->GetUsers().size())); + return false; + } + } + channel->SetMode(this, true); + return true; + } + else + { + return false; + } + } + else + { + if (channel->IsModeSet(this)) + { + channel->SetMode(this, false); + return true; + } + + return false; + } + } +}; + +/** Handle user mode +z +*/ +class SSLModeUser final + : public ModeHandler +{ +private: + UserCertificateAPI& API; + +public: + SSLModeUser(Module* Creator, UserCertificateAPI& api) + : ModeHandler(Creator, "sslqueries", 'z', PARAM_NONE, MODETYPE_USER) + , API(api) + { + } + + bool OnModeChange(User* user, User* dest, Channel* channel, Modes::Change& change) override + { + if (change.adding == dest->IsModeSet(this)) + return false; + + if (change.adding && IS_LOCAL(user) && (!API || !API->IsSecure(user))) + return false; + + dest->SetMode(this, change.adding); + return true; + } +}; + +class ModuleSSLModes final + : public Module + , public CTCTags::EventListener +{ +private: + UserCertificateAPI api; + CallerID::API calleridapi; + SSLMode sslm; + SSLModeUser sslquery; + FingerprintExtBan extban; + +public: + ModuleSSLModes() + : Module(VF_VENDOR, "Adds channel mode z (sslonly) which prevents users who are not connecting using TLS from joining the channel and user mode z (sslqueries) to prevent messages from non-TLS users.") + , CTCTags::EventListener(this) + , api(this) + , calleridapi(this) + , sslm(this, api) + , sslquery(this, api) + , extban(this, api) + { + } + + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + { + if (!override && chan && chan->IsModeSet(sslm)) + { + if (!api) + { + user->WriteNumeric(ERR_SECUREONLYCHAN, cname, fmt::format("Cannot join channel; unable to determine if you are a TLS user (+{} is set)", + sslm.GetModeChar())); + return MOD_RES_DENY; + } + + if (!api->IsSecure(user)) + { + user->WriteNumeric(ERR_SECUREONLYCHAN, cname, fmt::format("Cannot join channel; TLS users only (+{} is set)", + sslm.GetModeChar())); + return MOD_RES_DENY; + } + } + + return MOD_RES_PASSTHRU; + } + + ModResult HandleMessage(User* user, const MessageTarget& msgtarget) + { + if (msgtarget.type != MessageTarget::TYPE_USER) + return MOD_RES_PASSTHRU; + + auto* target = msgtarget.Get<User>(); + + /* If one or more of the parties involved is a services user, we won't stop it. */ + if (user->server->IsService() || target->server->IsService()) + return MOD_RES_PASSTHRU; + + /* If the target is +z */ + if (target->IsModeSet(sslquery)) + { + if (!api || !api->IsSecure(user) || (calleridapi && !calleridapi->IsOnAcceptList(user, target))) + { + /* The sending user is not on an TLS connection */ + user->WriteNumeric(Numerics::CannotSendTo(target, "messages", &sslquery)); + return MOD_RES_DENY; + } + } + /* If the user is +z */ + else if (user->IsModeSet(sslquery)) + { + if (!api || !api->IsSecure(target)) + { + user->WriteNumeric(Numerics::CannotSendTo(target, "messages", &sslquery, true)); + return MOD_RES_DENY; + } + } + + return MOD_RES_PASSTHRU; + } + + ModResult OnUserPreMessage(User* user, MessageTarget& target, MessageDetails& details) override + { + return HandleMessage(user, target); + } + + ModResult OnUserPreTagMessage(User* user, MessageTarget& target, CTCTags::TagMessageDetails& details) override + { + return HandleMessage(user, target); + } +}; + +MODULE_INIT(ModuleSSLModes) |
