harden: fix reachable panics (parse_duration/parse_iso/dechunk char-boundary+overflow), s2s netburst key/limit loss, rpc set_nick/set_vhost/notice injection, webirc rehash reload, panic-state reset, ws line cap, remote nick collision, per-conn state leaks
This commit is contained in:
parent
3fa9737ccb
commit
e935ee7002
12 changed files with 87 additions and 11 deletions
|
|
@ -28,6 +28,11 @@ impl Module for BlockAmsg {
|
|||
fn name(&self) -> &'static str {
|
||||
"blockamsg"
|
||||
}
|
||||
fn on_user_quit(&mut self, s: &mut Server, uid: Uid, _reason: &str) {
|
||||
if let Some(m) = s.ext.get_mut::<LastMsg>() {
|
||||
m.0.remove(&uid);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_pre_command(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ impl Module for CloudflareChallenge {
|
|||
fn name(&self) -> &'static str {
|
||||
"cloudflare_challenge"
|
||||
}
|
||||
fn on_user_quit(&mut self, s: &mut Server, uid: Uid, _reason: &str) {
|
||||
if let Some(p) = s.ext.get_mut::<Passed>() {
|
||||
p.0.remove(&uid);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_user_register(&mut self, srv: &mut Server, uid: Uid) -> ModResult {
|
||||
if !enabled(srv) || srv.is_oper(uid) {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ impl Module for ReCaptcha {
|
|||
fn name(&self) -> &'static str {
|
||||
"recaptcha"
|
||||
}
|
||||
fn on_user_quit(&mut self, s: &mut Server, uid: Uid, _reason: &str) {
|
||||
if let Some(v) = s.ext.get_mut::<Verified>() {
|
||||
v.0.remove(&uid);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_user_register(&mut self, srv: &mut Server, uid: Uid) -> ModResult {
|
||||
if !enabled(srv) || srv.is_oper(uid) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
|
|||
.ok_or_else(|| RpcError::invalid_params("missing 'target'"))?;
|
||||
let text = json::get_str(params, "message")
|
||||
.ok_or_else(|| RpcError::invalid_params("missing 'message'"))?;
|
||||
// strip CR/LF so a crafted message/target can't inject extra IRC lines
|
||||
let strip = |v: String| -> String { v.chars().filter(|c| *c != '\r' && *c != '\n').collect() };
|
||||
let (target, text) = (strip(target), strip(text));
|
||||
let src = s.name.clone();
|
||||
if target == "*" || target == "$*" {
|
||||
let uids: Vec<crate::Uid> = s.users.keys().copied().collect();
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
|
|||
let host = json::get_str(params, "vhost")
|
||||
.or_else(|| json::get_str(params, "host"))
|
||||
.ok_or_else(|| RpcError::invalid_params("missing 'vhost'"))?;
|
||||
if !crate::users::valid_host(&host) {
|
||||
return Err(RpcError::invalid_params("invalid vhost"));
|
||||
}
|
||||
s.change_host_ident(uid, None, Some(&host));
|
||||
Ok(obj(&[("result", "true".into())]))
|
||||
}
|
||||
|
|
@ -114,6 +117,16 @@ pub fn handle(s: &mut Server, action: &str, params: &str) -> Result<String, RpcE
|
|||
let uid = resolve(s, params).ok_or_else(|| RpcError::not_found("no such user"))?;
|
||||
let newnick = json::get_str(params, "newnick")
|
||||
.ok_or_else(|| RpcError::invalid_params("missing 'newnick'"))?;
|
||||
// validate + collision-check like the NICK / SVSNICK paths: an invalid or
|
||||
// taken nick would otherwise inject into the wire or hijack the nick index
|
||||
if !crate::users::valid_nick(&newnick, s.conf_num("maxnick", 30usize)) {
|
||||
return Err(RpcError::invalid_params("invalid nick"));
|
||||
}
|
||||
if s.find_nick(&newnick).is_some_and(|o| o != uid)
|
||||
|| s.remote_nick.contains_key(&newnick.to_ascii_lowercase())
|
||||
{
|
||||
return Err(RpcError::invalid_params("nick in use"));
|
||||
}
|
||||
s.set_nick(uid, &newnick);
|
||||
Ok(obj(&[("result", "true".into())]))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue