logtail: Inbound log takes Postfix's own tail, not a recipient's (audit M2)

Security audit 2026-10-17, finding M2, stage 0 - proven on the live server:
Postfix quotes a refused recipient in its reply in internal form (quotes
removed, "; < > =" kept: extract_addr, tok822_internalize), so
RCPT TO:<"; from=<ceo@…> to=<board@…> proto=ESMTP helo=<x>; "@domain>
spelled a whole tail inside the reply, and inboundRejectRe, whose reply
ended at the FIRST "; from=<", took the stranger's sender, recipient and
inbound domain for the row.

Now the reply is greedy and the tail anchored: "; from=<…> to=<…>
proto=\S+", a HELO name holding none of Postfix's NEUTER_CHARACTERS
(smtpd.c:1634, helo_cmd neuters ` <>()\";@`), and one-word SASL fields to
the end of the line (a `.*` SASL tail would have let a quoted MAIL FROM
spell the whole tail). Belt and braces: a reply that still holds
"; from=<" - a line with two tails - makes no row, so neither a recipient
nor an un-neutered HELO name can choose one. The quote/bracket/space
filter on from/to stays.

Tests: TestInboundRecipientCannotWriteTheRow (the live line's shape, and
with a forged MAIL FROM too); TestInboundHeloCannotWriteTheRow now feeds
HELO names as Postfix logs them (neutered) and expects no row for the
un-neutered ones. go test ./internal/logtail on Windows (the three known
Windows-only Follow failures aside).

Docs: security.md § Mail path; CHANGELOG; plan line ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-17 21:33:12 +03:00
co-authored by Claude Opus 5.5
parent f2ef705470
commit 683fdafc6d
5 changed files with 90 additions and 24 deletions
+5 -2
View File
@@ -697,10 +697,13 @@ the argument.
in postfix-config.sh:538,545; an e2e case plants a link and expects the
log line. — **Opus**, 0.5 h. *Done 2026-10-17:* TestStartsOnA28Data
plants a dangling `dmarc_transport` with DMARC reports on.
- [ ] **M2.** Greedy reply group anchored on Postfix's own tail; the live
- [x] **M2.** Greedy reply group anchored on Postfix's own tail; the live
line above as a test; security.md § Mail path reworded ("the last `;
from=<`, which is Postfix's because a HELO name can hold none of
`<>;\"`"). — **Opus**, 1 h.
`<>;\"`"). — **Opus**, 1 h. *Done 2026-10-17:* the HELO name in the
tail may hold none of the neutered characters and each SASL field is one
word (a `.*` SASL tail would have let a quoted sender spell the whole
tail); TestInboundRecipientCannotWriteTheRow.
- [ ] **M5.** Keep at most two `From:`/`Sender:` values; security.md:810
reworded. — **Opus**, 0.5 h.
- [ ] **A4.** Duplicate-cookie line through `loggedPath`. — **Sonnet**, 0.25 h.