From 4248cc0479c376a39388f794bb824cbcdcef5118 Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 01:01:33 +0000 Subject: [PATCH] =?UTF-8?q?oper:=20gate=20the=20client-facing=20SVSLOGIN/S?= =?UTF-8?q?VSLOGOUT=20behind=20oper=5Fsvslogin=20(default=20off)=20?= =?UTF-8?q?=E2=80=94=20they=20were=20a=20pre-S2S=20stopgap=20that=20let=20?= =?UTF-8?q?any=20oper=20forge=20an=20arbitrary=20account=20login=20(set=5F?= =?UTF-8?q?login)=20and=20thus=20+r/account-gated=20access;=20the=20live?= =?UTF-8?q?=20path=20is=20S2S=20link=5Fsvslogin=20(source=5Fis=5Fservice-g?= =?UTF-8?q?ated),=20so=20services=20are=20unaffected=20=E2=80=94=20only=20?= =?UTF-8?q?the=20obsolete=20oper=20backdoor=20is=20closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/coremods/core_oper.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/coremods/core_oper.rs b/src/coremods/core_oper.rs index 750a976..3709d72 100644 --- a/src/coremods/core_oper.rs +++ b/src/coremods/core_oper.rs @@ -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 ` logs a user in (`account` of `*`/`0` -/// logs out); `SVSLOGOUT ` 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 ` logs a user in (`account` of `*`/`0` logs out); +/// `SVSLOGOUT ` 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) = (¶ms[0], ¶ms[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(¶ms[0]) else { s.numeric( uid,