oper: TLS client-cert fingerprint login — oper block gains an optional fp=<sha256>; password=* means cert-only. Named OperBlock struct replaces the (name,pass,level) tuple. (Password login was never broken — verified live.)
This commit is contained in:
parent
853be58d18
commit
c4456cf002
6 changed files with 66 additions and 15 deletions
|
|
@ -308,7 +308,7 @@ impl Command for Stats {
|
|||
);
|
||||
}
|
||||
'o' => {
|
||||
let opers: Vec<String> = s.opers.iter().map(|(n, _, _)| n.clone()).collect();
|
||||
let opers: Vec<String> = s.opers.iter().map(|o| o.name.clone()).collect();
|
||||
for n in opers {
|
||||
s.numeric(uid, RPL_STATSOLINE, &format!("O * * {n} :oper"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,15 +123,37 @@ impl Command for Oper {
|
|||
}
|
||||
fn handle(&self, s: &mut Server, uid: Uid, params: &[String]) -> CmdResult {
|
||||
let (name, pass) = (params[0].clone(), params[1].clone());
|
||||
let Some((hash, level)) = s
|
||||
.opers
|
||||
.iter()
|
||||
.find(|(n, _, _)| *n == name)
|
||||
.map(|(_, p, lvl)| (p.clone(), *lvl))
|
||||
else {
|
||||
let Some(block) = s.opers.iter().find(|o| o.name == name).cloned() else {
|
||||
s.numeric(uid, ERR_PASSWDMISMATCH, ":Password incorrect");
|
||||
return CmdResult::Fail;
|
||||
};
|
||||
let (hash, level) = (block.password.clone(), block.level);
|
||||
// fingerprint login: the block demands a specific TLS client-cert SHA-256
|
||||
// fingerprint, so the user must be on a matching certificate.
|
||||
if let Some(want_fp) = &block.fingerprint {
|
||||
let user_fp = s.users.get(&uid).and_then(|u| u.certfp.clone());
|
||||
if !user_fp
|
||||
.as_deref()
|
||||
.is_some_and(|f| f.eq_ignore_ascii_case(want_fp))
|
||||
{
|
||||
s.snotice_c(
|
||||
'o',
|
||||
&format!("Failed OPER for {name}: certificate fingerprint mismatch"),
|
||||
);
|
||||
s.numeric(
|
||||
uid,
|
||||
ERR_PASSWDMISMATCH,
|
||||
":Password incorrect (a matching TLS client certificate is required)",
|
||||
);
|
||||
return CmdResult::Fail;
|
||||
}
|
||||
}
|
||||
// `password = *` means cert-only: the fingerprint above is the whole check.
|
||||
if hash == "*" {
|
||||
s.oper_up(uid);
|
||||
crate::modules::operlevels::set(s, uid, level);
|
||||
return CmdResult::Ok;
|
||||
}
|
||||
// a KDF password (bcrypt / pbkdf2) is slow — verify it off the core thread
|
||||
// (result arrives as OperAuth), so it can't freeze the server or be a DoS.
|
||||
if crate::modules::password_hash::is_slow(&hash) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue