sender check: the sender map read and looked up as Postfix does it
Postfix 3.7.11 takes the owners of the first key mail_addr_find() finds -
the address, then without its extension, then @domain, each tried only
when the one before is absent (src/global/mail_addr_find.c,
mail_addr_find_opt()) - and compares the login with them by
strcasecmp_utf8() (src/smtpd/smtpd_check.c,
reject_auth_sender_login_mismatch()). The check accepted a From: address
when any of the three keys named the application: an application holding
@domain could write an address listed for another application of the
domain, which Postfix refuses it in the envelope, and have it signed
(assumptions B-PS1).
The table is now read as dict_thash_open() reads it: continuation lines,
owners split on commas and blanks (B-PS4), the key ending at the first
blank outside quotes, keys folded; a key given twice makes Postfix refuse
the whole table (no DICT_FLAG_DUP_* flag), and the check then defers too.
The writer folds keys, so two cases of one address cannot be two lines.
e2e on the stand (2026-10-14, make e2e, exit 0): the wildcard application
is refused billing@ - listed for app-list - in From: (550 5.7.1), and in
the envelope by Postfix at RCPT ("553 5.7.1 <billing@...>: Sender address
rejected: not owned by user"). go vet and go test ./... pass on the
server (23 packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,10 +100,17 @@ func renderSenderLoginMaps(bindings []Binding) ([]byte, error) {
|
||||
if err := assertMapSafe(b.Address, b.Login); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, seen := byAddr[b.Address]; !seen {
|
||||
order = append(order, b.Address)
|
||||
// One line per key as Postfix folds it: smtpd opens the map with
|
||||
// DICT_FLAG_FOLD_FIX and no DICT_FLAG_DUP_* flag, so two keys that
|
||||
// differ only in case would make the whole table unusable (Postfix
|
||||
// 3.7.11, src/util/dict_thash.c, dict_thash_open()). The panel stores
|
||||
// addresses in lower case already; this keeps the file safe if a row
|
||||
// ever is not.
|
||||
addr := strings.ToLower(b.Address)
|
||||
if _, seen := byAddr[addr]; !seen {
|
||||
order = append(order, addr)
|
||||
}
|
||||
byAddr[b.Address] = appendUnique(byAddr[b.Address], b.Login)
|
||||
byAddr[addr] = appendUnique(byAddr[addr], b.Login)
|
||||
}
|
||||
sort.Strings(order)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user