Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

ircd.rs / docs / superpowers / plans / 2026-06-20-p11-slice294-okick.md
5.7 kB

P11 slice 294 — OKICK (operator force-kick bypassing chanop status)#

What & why#

charybdis extensions/m_okick.c: an operator force-kick. OKICK <channel> <user> [:comment] forcibly removes user from channel without requiring the oper to be a chanop — or even a member of the channel, sourced from the server so the channel sees :<server> KICK <chan> <user> :<comment>. It is the oper-override sibling of KICK (chanop-gated, slice baseline) and REMOVE (chanop force-part, slice 290), and the kick-shaped member of the charybdis operator channel-intervention family (SAJOIN/SAPART/SANICK/SAMODE/OJOIN/OPME/REMOVE).

Distinct from everything that exists: KICK/REMOVE enforce chanop + rank protection; the SA* family forces part/join/nick/mode. There is no oper-override visible-KICK yet. OKICK fills it.

Gate order (command/okick.rs)#

  1. Not an IRC operator (+o/+O) → 481 ERR_NOPRIVILEGES (OPME/SAJOIN precedent: plain oper bit, not a per-command OperPrivilege — charybdis m_okick is plain mg_not_oper too).
  2. Missing channel or user param → 461 ERR_NEEDMOREPARAMS ("OKICK").
  3. Unknown victim nick → 401 ERR_NOSUCHNICK.
  4. Channel does not exist → 403 ERR_NOSUCHCHANNEL.
  5. Victim not on the channel → 441 ERR_USERNOTINCHANNEL.
  6. Victim holds +Y (official-join) → 482 ERR_CHANOPRIVSNEEDED "official network business" (divergence from charybdis's raw removal: leveva's +Y immunity is documented absolute, even an oper override respects it — keeps the [[leveva-ojoin-official-join]] invariant true).
  7. Success → remove victim, broadcast :<server> KICK <chan> <victim> :<comment> to every local member (incl. the victim), echo the KICK back to the oper (always — the oper need not be a member; excluded from the audience fan to avoid a duplicate), relay server-sourced S2S, and post a +s audit snomask.

Comment defaults to the oper's nick (KICK precedent) when absent.

Channel seam — Channels::okick (the fuzz seam)#

New atomic seam, single lock, no permission/rank check (that is the point):

pub enum OkickOutcome {
    NoSuchChannel,
    NotInChannel { display: String },
    OfficialBusiness { display: String },
    Kicked { display: String, audience: Vec<Uid> },
}
pub fn okick(&self, name: &str, victim: &Uid) -> OkickOutcome
  • channel missing → NoSuchChannel
  • victim not a member → NotInChannel { display }
  • victim +YOfficialBusiness { display }
  • else remove victim, return Kicked { display, audience(pre-removal roster) }, delete the channel if now empty (destroy_when_empty).

S2S — s2s::relay::server_kick#

New helper mirroring server_channel_mode (server-sourced): broadcast :<our_sid> KICK <chan> <victim_uid> :<reason>. Inbound inbound_kick already accepts a SID prefix via source_mask (renders the server name), so the path is symmetric — no inbound change needed.

Divergences (documented)#

  • leveva-native — IRCnet 2.11 has no OKICK (charybdis extension); no oracle, no differential.
  • Plain oper-bit gate — any oper, not a per-command OperPrivilege (OPME/OLIST/TESTLINE precedent); charybdis OKICK has no distinct privilege either.
  • +Y immunity respected — charybdis OKICK raw-removes (bypasses everything); leveva keeps the +Y official-join absolute-immunity invariant, refusing with 482. Documented, deliberate.
  • Oper always gets the KICK echo — even when not a member (every leveva handler confirms to the actor); charybdis sends the oper nothing extra when they are not on the channel.
  • +s audit snomask not +w wallops — SA*/OJOIN/OPME family precedent (slices 184/186/292); charybdis sends a wallops. The audit wording is leveva's.
  • Local-only — removal of a local-or-remote member of this server's channel; the S2S relay carries the removal onward, but OKICK itself is a per-server command (charybdis is per-server).

Tests (TDD; inverse invariants + fuzzing)#

  • channel.rs units: okick_removes_any_member_without_a_permission_check (bypasses chanop/rank — a plain member is removed, the roster shrinks, channel survives; inverse — re-join is clean); okick_refuses_an_official_join_member (+YOfficialBusiness, member stays); the missing-channel/missing-member arms; last-member removal deletes the channel.
  • command/okick.rs units: 481 non-oper (no removal); 461 missing args; 401 unknown nick; 403 ghost channel; 441 victim-not-on-channel; 482 +Y victim; success (oper not a member) → server-sourced KICK echoed to oper + co-member sees it + victim gone; default comment = oper nick; inverse — the gated/refused paths change nothing.
  • tests/okick_proptest.rs (2 props): okick_outcome_matches_channel_state (random member/op/+Y
    • ghost target → exact outcome; victim present afterwards iff not removed) + arbitrary_args_never_panic (replies ⊆ {481, 461, 401, 403, 441, 482, KICK}).
  • tests/golden_okick.rs (real binary, dline.kdl oper): bob owns #rust (carol a plain member); alice opers but never joins; non-oper bob OKICK481; alice OKICK #rust carol :spam → server-sourced KICK seen by bob/carol + echoed to alice; carol gone; OKICK #ghost x403.

Wiring#

  • mod okick; + "OKICK" => okick::okick(...) in command/mod.rs.
  • help/OKICK.md + OKICK in help.rs COMMANDS allowlist + the operator line in help/index.md.

Gate#

cargo test -p leveva green; cargo clippy -p leveva --tests clean; cargo build --workspace 0 warnings.