Report failed IDENTIFY against a password-less account to the auth feed
All checks were successful
CI / check (push) Successful in 4m10s
All checks were successful
CI / check (push) Successful in 4m10s
This commit is contained in:
parent
876bc55e2b
commit
1ecb76affb
3 changed files with 38 additions and 0 deletions
|
|
@ -485,6 +485,21 @@ pub struct ServiceCtx {
|
||||||
// engine drains these to the log channel, prefixing the sender's nick/host —
|
// engine drains these to the log channel, prefixing the sender's nick/host —
|
||||||
// e.g. a blocked look-alike registration attempt.
|
// e.g. a blocked look-alike registration attempt.
|
||||||
pub alerts: Vec<(String, String)>,
|
pub alerts: Vec<(String, String)>,
|
||||||
|
// Login-attempt outcomes for the auth audit feed. The engine drains these
|
||||||
|
// through its own auth_report (which resolves the client's source), so a
|
||||||
|
// module can report a reject the engine's verify path never sees — e.g. a
|
||||||
|
// failed IDENTIFY against a cert-only account with no password verifier.
|
||||||
|
pub auth_reports: Vec<AuthReport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A login-attempt outcome a module hands back for the auth audit feed.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AuthReport {
|
||||||
|
pub ok: bool,
|
||||||
|
pub account: Option<String>,
|
||||||
|
pub mech: String,
|
||||||
|
pub client: String,
|
||||||
|
pub reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceCtx {
|
impl ServiceCtx {
|
||||||
|
|
@ -536,6 +551,19 @@ impl ServiceCtx {
|
||||||
self.alerts.push((category.into(), text.into()));
|
self.alerts.push((category.into(), text.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report a login-attempt outcome to the auth audit feed. For a reject the
|
||||||
|
// engine's own verify path won't produce (a failed IDENTIFY against an account
|
||||||
|
// with no password verifier), so operators see it like any other failed login.
|
||||||
|
pub fn auth_report(&mut self, ok: bool, account: Option<&str>, mech: &str, client: &str, reason: Option<&str>) {
|
||||||
|
self.auth_reports.push(AuthReport {
|
||||||
|
ok,
|
||||||
|
account: account.map(String::from),
|
||||||
|
mech: mech.to_string(),
|
||||||
|
client: client.to_string(),
|
||||||
|
reason: reason.map(String::from),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// A channel/target message sourced from one of our pseudo-clients (e.g. a
|
// A channel/target message sourced from one of our pseudo-clients (e.g. a
|
||||||
// bot answering a fantasy command, or BotServ SAY).
|
// bot answering a fantasy command, or BotServ SAY).
|
||||||
pub fn privmsg(&mut self, from: &str, to: &str, text: impl Into<String>) {
|
pub fn privmsg(&mut self, from: &str, to: &str, text: impl Into<String>) {
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,11 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
||||||
match db.scram_verifier(account_name) {
|
match db.scram_verifier(account_name) {
|
||||||
None => {
|
None => {
|
||||||
// Exists but has no verifier (e.g. cert-only) — a password can't match.
|
// Exists but has no verifier (e.g. cert-only) — a password can't match.
|
||||||
|
// Report it to the auth feed too, or probing a cert-only account is a
|
||||||
|
// blind spot the deferred wrong-password path doesn't have.
|
||||||
db.note_auth(account_name, false);
|
db.note_auth(account_name, false);
|
||||||
ctx.count("nickserv.identify_fail");
|
ctx.count("nickserv.identify_fail");
|
||||||
|
ctx.auth_report(false, Some(account_name), "NickServ IDENTIFY", from.uid, Some("no password set"));
|
||||||
ctx.fail(me, from.uid, "IDENTIFY", "INVALID_CREDENTIALS", "Invalid password. Please try again.");
|
ctx.fail(me, from.uid, "IDENTIFY", "INVALID_CREDENTIALS", "Invalid password. Please try again.");
|
||||||
}
|
}
|
||||||
Some((account, verifier)) => {
|
Some((account, verifier)) => {
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,13 @@ impl Engine {
|
||||||
out.push(line);
|
out.push(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Login-attempt outcomes a command reported (e.g. a failed IDENTIFY against
|
||||||
|
// a cert-only account), routed through the same auth feed as the verify path.
|
||||||
|
for r in std::mem::take(&mut ctx.auth_reports) {
|
||||||
|
if let Some(line) = self.auth_report(r.ok, r.account.as_deref(), &r.mech, &r.client, r.reason.as_deref()) {
|
||||||
|
out.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.apply_dict_limit(&mut out);
|
self.apply_dict_limit(&mut out);
|
||||||
self.apply_msg_style(&mut out);
|
self.apply_msg_style(&mut out);
|
||||||
out
|
out
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue