mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-09 10:39:02 -04:00
Move <oper:autologin> from m_sslinfo to core_oper and rework.
- Promote autologin to a core concept with visibility in events. - Replace the binary yes/no value with strict/relaxed/never. This intentionally breaks v3 oper block autologin as admins will need to review them for the security implications of the new behaviour.
This commit is contained in:
parent
cd6329d4b0
commit
3c6e24665f
@ -142,10 +142,18 @@
|
||||
# If the sslinfo module isn't loaded, this option will be ignored.
|
||||
#fingerprint="67cb9dc013248a829bb2171ed11becd4"
|
||||
|
||||
# autologin: If a TLS client certificate fingerprint for this oper is specified,
|
||||
# you can have the oper block automatically log in if they match the fingerprint
|
||||
# and host fields. Requires the sslinfo module.
|
||||
#autologin="yes"
|
||||
# autologin: Whether to automatically log this server operator in on connect if all
|
||||
# of their details match the ones in this <oper> block. Can be set to "strict" to
|
||||
# automatically log in if the user's nickname matches the oper account name and the
|
||||
# account/host/sslonly/etc fields match, "relaxed" to automatically log in if the
|
||||
# account/host/sslonly/etc fields match, and "never" to not allow automatically
|
||||
# logging in to this oper account. Defaults to "never".
|
||||
#
|
||||
# IMPORTANT: As this option overrides the password field it should **NOT** be used
|
||||
# unless you are certain that nobody other than the intended user will match the
|
||||
# restrictions of this <oper> block. Failure to do this may result in your server
|
||||
# being compromised.
|
||||
#autologin="strict"
|
||||
|
||||
# sslonly: If enabled, this oper can only oper up if they're using a TLS connection.
|
||||
# Setting this option adds a decent bit of security. Highly recommended
|
||||
|
@ -925,21 +925,24 @@ public:
|
||||
/** Called when a local user is attempting to log in to an server operator account.
|
||||
* @param user The user who is attempting to log in.
|
||||
* @param oper The server operator account they are attempting to log in to.
|
||||
* @param automatic Whether the login attempt is being performed automatically.
|
||||
* @return MOD_RES_ALLOW to explicitly allow the login, MOD_RES_DENY to explicitly deny the
|
||||
* login, or MOD_RES_PASSTHRU to let another module handle the event.
|
||||
*/
|
||||
virtual ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper);
|
||||
virtual ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper, bool automatic);
|
||||
|
||||
/** Called when a user is about to be logged in to an server operator account.
|
||||
* @param user The user who is about to be logged in.
|
||||
* @param oper The server operator account they are logging in to.
|
||||
* @param automatic Whether the login was performed automatically.
|
||||
*/
|
||||
virtual void OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper);
|
||||
virtual void OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper, bool automatic);
|
||||
|
||||
/** Called after a user has been logged in to an server operator account.
|
||||
* @param user The user who has been logged in.
|
||||
* @param automatic Whether the login was performed automatically.
|
||||
*/
|
||||
virtual void OnPostOperLogin(User* user);
|
||||
virtual void OnPostOperLogin(User* user, bool automatic);
|
||||
|
||||
/** Called when a user is about to be logged out of an server operator account.
|
||||
* @param user The user who is about to be logged out.
|
||||
|
@ -236,7 +236,24 @@ public:
|
||||
class CoreExport OperAccount
|
||||
: public OperType
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
/** Possible states for whether an oper account can be automatically logged into. */
|
||||
enum class AutoLogin
|
||||
: uint8_t
|
||||
{
|
||||
/** Users can automatically log in to this account if they match all fields and their nick matches the account name. */
|
||||
STRICT,
|
||||
|
||||
/** Users can automatically log in to this account if they match all fields. */
|
||||
RELAXED,
|
||||
|
||||
/** Users can not automatically log in to this account. */
|
||||
NEVER,
|
||||
};
|
||||
|
||||
/** Whether this oper account can be automatically logged into. */
|
||||
AutoLogin autologin;
|
||||
|
||||
/** The password to used to log into this oper account. */
|
||||
std::string password;
|
||||
|
||||
@ -254,6 +271,9 @@ public:
|
||||
*/
|
||||
OperAccount(const std::string& n, const std::shared_ptr<OperType>& o, const std::shared_ptr<ConfigTag>& t);
|
||||
|
||||
/** Check whether this user can attempt to automatically log in to this account. */
|
||||
bool CanAutoLogin(LocalUser* user) const;
|
||||
|
||||
/** Check the specified password against the one from this oper account's password.
|
||||
* @param pw The password to check.
|
||||
*/
|
||||
@ -581,10 +601,11 @@ public:
|
||||
|
||||
/** Logs this user into the specified server operator account.
|
||||
* @param account The account to log this user in to.
|
||||
* @param automatic Whether this is an automatic login attempt.
|
||||
* @param force Whether to ignore any checks from OnPreOperLogin.
|
||||
* @return True if the user is logged into successfully; otherwise, false.
|
||||
*/
|
||||
bool OperLogin(const std::shared_ptr<OperAccount>& account, bool force = false);
|
||||
bool OperLogin(const std::shared_ptr<OperAccount>& account, bool automatic = false, bool force = false);
|
||||
|
||||
/** Logs this user out of their server operator account. Does nothing to non-operators. */
|
||||
void OperLogout();
|
||||
|
@ -63,18 +63,38 @@ public:
|
||||
cmdkill.hideservicekills = security->getBool("hideservicekills", security->getBool("hideulinekills"));
|
||||
}
|
||||
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper) override
|
||||
void OnPostConnect(User* user) override
|
||||
{
|
||||
LocalUser* luser = IS_LOCAL(user);
|
||||
if (!luser)
|
||||
return;
|
||||
|
||||
// Find an auto-oper block for this user.
|
||||
for (const auto& [_, account] : ServerInstance->Config->OperAccounts)
|
||||
{
|
||||
if (!account->CanAutoLogin(luser))
|
||||
continue; // No autologin for this account.
|
||||
|
||||
if (user->OperLogin(account, true))
|
||||
break; // Successfully logged in to the account.
|
||||
}
|
||||
}
|
||||
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper, bool automatic) override
|
||||
{
|
||||
const std::string hosts = oper->GetConfig()->getString("host");
|
||||
if (InspIRCd::MatchMask(hosts, user->MakeHost(), user->MakeHostIP()))
|
||||
return MOD_RES_PASSTHRU; // Host matches.
|
||||
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are connecting from the wrong user@host.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
if (!automatic)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are connecting from the wrong user@host.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
}
|
||||
return MOD_RES_DENY; // Host does not match.
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
LocalUser* luser = IS_LOCAL(user);
|
||||
if (!luser)
|
||||
@ -84,9 +104,9 @@ public:
|
||||
strchr("AEIOUaeiou", user->oper->GetType()[0]) ? "an" : "a",
|
||||
user->oper->GetType().c_str()));
|
||||
|
||||
ServerInstance->SNO.WriteToSnoMask('o', "%s (%s) is now a server operator of type %s (using account %s)",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->oper->GetType().c_str(),
|
||||
user->oper->GetName().c_str());
|
||||
ServerInstance->SNO.WriteToSnoMask('o', "%s (%s) [%s] is now a server operator of type \x02%s\x02 (%susing account \x02%s\x02).",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), user->oper->GetType().c_str(),
|
||||
automatic ? "automatically " : "", user->oper->GetName().c_str());
|
||||
|
||||
const std::string vhost = luser->oper->GetConfig()->getString("vhost");
|
||||
if (!vhost.empty())
|
||||
|
@ -162,9 +162,9 @@ void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); }
|
||||
void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); }
|
||||
ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; }
|
||||
void Module::OnShutdown(const std::string& reason) { DetachEvent(I_OnShutdown); }
|
||||
ModResult Module::OnPreOperLogin(LocalUser*, const std::shared_ptr<OperAccount>&) { DetachEvent(I_OnPreOperLogin); return MOD_RES_PASSTHRU; }
|
||||
void Module::OnOperLogin(User*, const std::shared_ptr<OperAccount>&) { DetachEvent(I_OnOperLogin); }
|
||||
void Module::OnPostOperLogin(User*) { DetachEvent(I_OnPostOperLogin); }
|
||||
ModResult Module::OnPreOperLogin(LocalUser*, const std::shared_ptr<OperAccount>&, bool) { DetachEvent(I_OnPreOperLogin); return MOD_RES_PASSTHRU; }
|
||||
void Module::OnOperLogin(User*, const std::shared_ptr<OperAccount>&, bool) { DetachEvent(I_OnOperLogin); }
|
||||
void Module::OnPostOperLogin(User*, bool) { DetachEvent(I_OnPostOperLogin); }
|
||||
void Module::OnOperLogout(User*) { DetachEvent(I_OnOperLogout); }
|
||||
void Module::OnPostOperLogout(User*, const std::shared_ptr<OperAccount>&) { DetachEvent(I_OnPostOperLogout); }
|
||||
|
||||
|
@ -338,7 +338,7 @@ public:
|
||||
return MOD_RES_PASSTHRU;
|
||||
}
|
||||
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper) override
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper, bool automatic) override
|
||||
{
|
||||
const std::string accountstr = oper->GetConfig()->getString("account");
|
||||
if (accountstr.empty())
|
||||
@ -357,8 +357,11 @@ public:
|
||||
return MOD_RES_PASSTHRU; // Matches on account name.
|
||||
}
|
||||
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not logged into the correct user account.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
if (!automatic)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not logged into the correct user account.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
}
|
||||
return MOD_RES_DENY; // Account required but it does not match.
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
operChans.push_back(channame);
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
LocalUser* localuser = IS_LOCAL(user);
|
||||
if (!localuser)
|
||||
|
@ -35,7 +35,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
if (!IS_LOCAL(user))
|
||||
return; // We don't handle remote users.
|
||||
|
@ -136,7 +136,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
if (IS_LOCAL(user) && user->oper->GetConfig()->getBool("automotd", onoper))
|
||||
cmd.ShowOperMOTD(user, false);
|
||||
|
@ -104,7 +104,7 @@ public:
|
||||
ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, nullptr, changelist);
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode)))
|
||||
SetOperPrefix(user, true);
|
||||
|
@ -772,7 +772,7 @@ restart:
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleSpanningTree::OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper)
|
||||
void ModuleSpanningTree::OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper, bool automatic)
|
||||
{
|
||||
if (!user->IsFullyConnected() || !IS_LOCAL(user))
|
||||
return;
|
||||
|
@ -196,7 +196,7 @@ public:
|
||||
void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) override;
|
||||
void OnPreRehash(User* user, const std::string& parameter) override;
|
||||
void ReadConfig(ConfigStatus& status) override;
|
||||
void OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper) override;
|
||||
void OnOperLogin(User* user, const std::shared_ptr<OperAccount>& oper, bool automatic) override;
|
||||
void OnAddLine(User* u, XLine* x) override;
|
||||
void OnDelLine(User* u, XLine* x) override;
|
||||
ModResult OnStats(Stats::Context& stats) override;
|
||||
|
@ -49,8 +49,9 @@ CmdResult CommandOpertype::HandleRemote(RemoteUser* u, CommandBase::Params& para
|
||||
return CmdResult::SUCCESS;
|
||||
}
|
||||
|
||||
ServerInstance->SNO.WriteToSnoMask('O', "From %s: %s (%s) is now a server operator of type %s",
|
||||
u->server->GetName().c_str(), u->nick.c_str(), u->MakeHost().c_str(), u->oper->GetType().c_str());
|
||||
ServerInstance->SNO.WriteToSnoMask('O', "From %s: %s (%s) [%s] is now a server operator of type \x02%s\x02.",
|
||||
u->server->GetName().c_str(), u->nick.c_str(), u->MakeHost().c_str(), u->GetIPString().c_str(),
|
||||
u->oper->GetType().c_str());
|
||||
return CmdResult::SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -323,21 +323,27 @@ public:
|
||||
return MOD_RES_PASSTHRU;
|
||||
}
|
||||
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper) override
|
||||
ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr<OperAccount>& oper, bool automatic) override
|
||||
{
|
||||
auto cert = cmd.sslapi.GetCertificate(user);
|
||||
if (oper->GetConfig()->getBool("sslonly") && !cert)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not connected using TLS.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
if (!automatic)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not connected using TLS.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
}
|
||||
return MOD_RES_DENY;
|
||||
}
|
||||
|
||||
const std::string fingerprint = oper->GetConfig()->getString("fingerprint");
|
||||
if (!fingerprint.empty() && (!cert || !MatchFP(cert, fingerprint)))
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not using the correct TLS client certificate.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
if (!automatic)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('o', "%s (%s) [%s] failed to log into the \x02%s\x02 oper account because they are not using the correct TLS client certificate.",
|
||||
user->nick.c_str(), user->MakeHost().c_str(), user->GetIPString().c_str(), oper->GetName().c_str());
|
||||
}
|
||||
return MOD_RES_DENY;
|
||||
}
|
||||
|
||||
@ -365,20 +371,6 @@ public:
|
||||
if (cert && !cert->GetFingerprint().empty())
|
||||
text.append(" and your TLS client certificate fingerprint is ").append(cert->GetFingerprint());
|
||||
user->WriteNotice(text);
|
||||
|
||||
if (!cert)
|
||||
return;
|
||||
|
||||
// Find an auto-oper block for this user
|
||||
for (const auto& [_, info] : ServerInstance->Config->OperAccounts)
|
||||
{
|
||||
const auto& oper = info->GetConfig();
|
||||
if (!oper->getBool("autologin"))
|
||||
continue; // No autologin for this block.
|
||||
|
||||
if (!user->OperLogin(info))
|
||||
continue; // Some other field does not match.
|
||||
}
|
||||
}
|
||||
|
||||
ModResult OnSetConnectClass(LocalUser* user, const ConnectClass::Ptr& myclass) override
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
return MOD_RES_PASSTHRU;
|
||||
}
|
||||
|
||||
void OnPostOperLogin(User* user) override
|
||||
void OnPostOperLogin(User* user, bool automatic) override
|
||||
{
|
||||
if (!IS_LOCAL(user))
|
||||
return;
|
||||
|
@ -349,13 +349,13 @@ Cullable::Result FakeUser::Cull()
|
||||
return User::Cull();
|
||||
}
|
||||
|
||||
bool User::OperLogin(const std::shared_ptr<OperAccount>& account, bool force)
|
||||
bool User::OperLogin(const std::shared_ptr<OperAccount>& account, bool automatic, bool force)
|
||||
{
|
||||
LocalUser* luser = IS_LOCAL(this);
|
||||
if (luser && !quitting && !force)
|
||||
{
|
||||
ModResult modres;
|
||||
FIRST_MOD_RESULT(OnPreOperLogin, modres, (luser, account));
|
||||
FIRST_MOD_RESULT(OnPreOperLogin, modres, (luser, account, automatic));
|
||||
if (modres == MOD_RES_DENY)
|
||||
return false; // Module rejected the oper attempt.
|
||||
}
|
||||
@ -364,7 +364,7 @@ bool User::OperLogin(const std::shared_ptr<OperAccount>& account, bool force)
|
||||
if (IsOper())
|
||||
OperLogout();
|
||||
|
||||
FOREACH_MOD(OnOperLogin, (this, account));
|
||||
FOREACH_MOD(OnOperLogin, (this, account, automatic));
|
||||
|
||||
// When a user logs in we need to:
|
||||
// 1. Set the operator account (this is what IsOper checks).
|
||||
@ -385,7 +385,7 @@ bool User::OperLogin(const std::shared_ptr<OperAccount>& account, bool force)
|
||||
}
|
||||
ServerInstance->Users.all_opers.push_back(this);
|
||||
|
||||
FOREACH_MOD(OnPostOperLogin, (this));
|
||||
FOREACH_MOD(OnPostOperLogin, (this, automatic));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1379,6 +1379,12 @@ OperAccount::OperAccount(const std::string& n, const std::shared_ptr<OperType>&
|
||||
, passwordhash(t->getString("hash", "plaintext", 1))
|
||||
, type(o ? o->GetName() : n)
|
||||
{
|
||||
autologin = t->getEnum("autologin", AutoLogin::NEVER, {
|
||||
{ "strict", AutoLogin::STRICT },
|
||||
{ "relaxed", AutoLogin::RELAXED },
|
||||
{ "never", AutoLogin::NEVER },
|
||||
});
|
||||
|
||||
if (o)
|
||||
{
|
||||
chanmodes = o->chanmodes;
|
||||
@ -1391,6 +1397,24 @@ OperAccount::OperAccount(const std::string& n, const std::shared_ptr<OperType>&
|
||||
Configure(t, true);
|
||||
}
|
||||
|
||||
bool OperAccount::CanAutoLogin(LocalUser* user) const
|
||||
{
|
||||
switch (autologin)
|
||||
{
|
||||
case AutoLogin::STRICT:
|
||||
return user->nick == GetName();
|
||||
|
||||
case AutoLogin::RELAXED:
|
||||
return true;
|
||||
|
||||
case AutoLogin::NEVER:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Should never be reached.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OperAccount::CheckPassword(const std::string& pw) const
|
||||
{
|
||||
return ServerInstance->PassCompare(password, pw, passwordhash);
|
||||
|
Loading…
x
Reference in New Issue
Block a user