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 —
|
||||
// e.g. a blocked look-alike registration attempt.
|
||||
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 {
|
||||
|
|
@ -536,6 +551,19 @@ impl ServiceCtx {
|
|||
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
|
||||
// bot answering a fantasy command, or BotServ SAY).
|
||||
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) {
|
||||
None => {
|
||||
// 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);
|
||||
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.");
|
||||
}
|
||||
Some((account, verifier)) => {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,13 @@ impl Engine {
|
|||
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_msg_style(&mut out);
|
||||
out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue