oper: gate the client-facing SVSLOGIN/SVSLOGOUT behind oper_svslogin (default off) — they were a pre-S2S stopgap that let any oper forge an arbitrary account login (set_login) and thus +r/account-gated access; the live path is S2S link_svslogin (source_is_service-gated), so services are unaffected — only the obsolete oper backdoor is closed

This commit is contained in:
Jean Chevronnet 2026-08-19 01:01:33 +00:00
parent a80b86b50d
commit 4248cc0479

View file

@ -254,10 +254,12 @@ impl Command for Kill {
}
/// SVSLOGIN / SVSLOGOUT — the services interface to the account layer
/// ([`crate::accounts`]). Over S2S these arrive from a services pseudoserver;
/// until S2S exists an oper may invoke them to drive `+r` and the account-gated
/// channel modes. `SVSLOGIN <nick> <account>` logs a user in (`account` of `*`/`0`
/// logs out); `SVSLOGOUT <nick>` logs them out.
/// ([`crate::accounts`]). The live path is S2S: a U-lined services server sources
/// them (see `link_svslogin`, gated by `source_is_service`). The client-facing
/// oper form is an emergency stopgap for a network with no services linked — it can
/// forge any account login, so it is OFF unless `oper_svslogin = yes`.
/// `SVSLOGIN <nick> <account>` logs a user in (`account` of `*`/`0` logs out);
/// `SVSLOGOUT <nick>` logs them out.
struct SvsLogin;
impl Command for SvsLogin {
fn name(&self) -> &'static str {
@ -275,6 +277,16 @@ impl Command for SvsLogin {
);
return CmdResult::Fail;
}
// The account layer is driven by services over S2S; the oper form can forge
// any login, so it's an opt-in emergency stopgap (default off).
if !s.conf_bool("oper_svslogin", false) {
s.numeric(
uid,
ERR_NOPRIVILEGES,
":Permission Denied- account login is handled by services (set oper_svslogin to override)",
);
return CmdResult::Fail;
}
let (target, account) = (&params[0], &params[1]);
let Some(tuid) = s.find_nick(target) else {
s.numeric(
@ -310,6 +322,14 @@ impl Command for SvsLogout {
);
return CmdResult::Fail;
}
if !s.conf_bool("oper_svslogin", false) {
s.numeric(
uid,
ERR_NOPRIVILEGES,
":Permission Denied- account login is handled by services (set oper_svslogin to override)",
);
return CmdResult::Fail;
}
let Some(tuid) = s.find_nick(&params[0]) else {
s.numeric(
uid,