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 /// SVSLOGIN / SVSLOGOUT — the services interface to the account layer
/// ([`crate::accounts`]). Over S2S these arrive from a services pseudoserver; /// ([`crate::accounts`]). The live path is S2S: a U-lined services server sources
/// until S2S exists an oper may invoke them to drive `+r` and the account-gated /// them (see `link_svslogin`, gated by `source_is_service`). The client-facing
/// channel modes. `SVSLOGIN <nick> <account>` logs a user in (`account` of `*`/`0` /// oper form is an emergency stopgap for a network with no services linked — it can
/// logs out); `SVSLOGOUT <nick>` logs them out. /// 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; struct SvsLogin;
impl Command for SvsLogin { impl Command for SvsLogin {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
@ -275,6 +277,16 @@ impl Command for SvsLogin {
); );
return CmdResult::Fail; 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 (target, account) = (&params[0], &params[1]);
let Some(tuid) = s.find_nick(target) else { let Some(tuid) = s.find_nick(target) else {
s.numeric( s.numeric(
@ -310,6 +322,14 @@ impl Command for SvsLogout {
); );
return CmdResult::Fail; 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 { let Some(tuid) = s.find_nick(&params[0]) else {
s.numeric( s.numeric(
uid, uid,