add +k servprotect umode (server-set-only, advertised in 004/umodes): blocks KILL, KICK and SA* on a network service; tracked for remote services too

This commit is contained in:
Jean Chevronnet 2026-08-16 03:00:25 +00:00
parent 91022cffe8
commit b7600ee5aa
5 changed files with 69 additions and 1 deletions

View file

@ -506,6 +506,15 @@ impl Command for Kick {
);
return CmdResult::Fail;
}
// servprotect (+k): a network service can't be kicked
if s.uid_servprotected(tuid) {
s.numeric(
uid,
ERR_CHANOPRIVSNEEDED,
&format!("{chan} :You cannot kick a network service"),
);
return CmdResult::Fail;
}
// can't kick someone who out-ranks you
if s.rank(uid, &key) < s.rank(tuid, &key) {
s.numeric(

View file

@ -28,6 +28,7 @@ pub fn svs_set_user_modes(s: &mut Server, tuid: Uid, modestring: &str) {
let mut sign = '+';
let mut applied = String::new();
let mut last = ' ';
s.mode_sudo = true; // services authority — allows server-only modes like +k
for c in modestring.chars() {
if c == '+' || c == '-' {
sign = c;
@ -40,6 +41,7 @@ pub fn svs_set_user_modes(s: &mut Server, tuid: Uid, modestring: &str) {
}
}
}
s.mode_sudo = false;
if !applied.is_empty() {
let nick = s
.users

View file

@ -186,6 +186,11 @@ impl Command for Kill {
);
return CmdResult::Fail;
};
// servprotect (+k): a network service can't be killed
if s.uid_servprotected(tuid) {
s.numeric(uid, ERR_NOPRIVILEGES, ":You cannot KILL a network service");
return CmdResult::Fail;
}
// operlevels: a lower-level oper can't KILL a higher-level oper
if let Some(reason) = crate::modules::operlevels::deny_kill(s, uid, tuid) {
s.numeric(uid, ERR_NOPRIVILEGES, &format!(":{reason}"));
@ -417,6 +422,7 @@ impl Command for SaNick {
);
return CmdResult::Fail;
};
if s.uid_servprotected(tuid) { s.numeric(uid, ERR_NOPRIVILEGES, ":Cannot use an SA command on a network service"); return CmdResult::Fail; }
let newnick = &params[1];
if !valid_nick(newnick, s.conf_num("maxnick", 30usize)) {
s.numeric(
@ -1174,6 +1180,10 @@ impl Command for SaKick {
);
return CmdResult::Fail;
}
if s.uid_servprotected(tuid) {
s.numeric(uid, ERR_NOPRIVILEGES, ":Cannot use an SA command on a network service");
return CmdResult::Fail;
}
let reason = params
.get(2)
.cloned()
@ -1221,6 +1231,7 @@ impl Command for SaQuit {
.get(1)
.cloned()
.unwrap_or_else(|| "Services forced quit".to_string());
if s.uid_servprotected(tuid) { s.numeric(uid, ERR_NOPRIVILEGES, ":Cannot use an SA command on a network service"); return CmdResult::Fail; }
s.send(tuid, format!("ERROR :Closing link: (SAQUIT: {reason})"));
s.remove_user(tuid, &format!("Quit: {reason}"));
let by = oper_nick(s, uid);