The security documentation's last open item of panel-dkim-signing (the owner's decision, 2026-10-16): go-milter v0.4.1 added every header of a session to a map of its own, emptied only on ABORT, and handed it to Headers - which no milter of the panel reads. Postfix bounds one header, not their number, so a message of nothing but headers up to the message size limit was held whole by the library in the sender check, the signer and the journal milter on 465/587 and in the inbound journal milter on port 25, where strangers send; the signer's 1 MiB refusal came after it was all in. third_party/go-milter is v0.4.1 from the module cache (BSD 2-Clause, LICENSE kept) with session.go changed: CodeHeader hands the header to Header and keeps nothing, CodeEOH hands Headers an empty map. PATCHES.md says what and why, and how to take a new upstream version. go.mod replaces the module with it; the Dockerfile copies third_party before "go mod download". TestTheLibraryKeepsNoHeaders holds it through the library's own client over two messages of one connection. Comments, security.md (the accepted risk becomes a note), development.md's library table, the plan and CHANGELOG follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
package milter
|
||||
|
||||
// Response represents a response structure returned by callback
|
||||
// handlers to indicate how the milter server should proceed
|
||||
type Response interface {
|
||||
Response() *Message
|
||||
Continue() bool
|
||||
}
|
||||
|
||||
// SimpleResponse type to define list of pre-defined responses
|
||||
type SimpleResponse byte
|
||||
|
||||
// Response returns a Message object reference
|
||||
func (r SimpleResponse) Response() *Message {
|
||||
return &Message{byte(r), nil}
|
||||
}
|
||||
|
||||
// Continue to process milter messages only if current code is Continue
|
||||
func (r SimpleResponse) Continue() bool {
|
||||
return ActionCode(r) == ActContinue
|
||||
}
|
||||
|
||||
// Define standard responses with no data
|
||||
const (
|
||||
RespAccept = SimpleResponse(ActAccept)
|
||||
RespContinue = SimpleResponse(ActContinue)
|
||||
RespDiscard = SimpleResponse(ActDiscard)
|
||||
RespReject = SimpleResponse(ActReject)
|
||||
RespTempFail = SimpleResponse(ActTempFail)
|
||||
)
|
||||
|
||||
// CustomResponse is a response instance used by callback handlers to indicate
|
||||
// how the milter should continue processing of current message
|
||||
type CustomResponse struct {
|
||||
code byte
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Response returns message instance with data
|
||||
func (c *CustomResponse) Response() *Message {
|
||||
return &Message{c.code, c.data}
|
||||
}
|
||||
|
||||
// Continue returns false if milter chain should be stopped, true otherwise
|
||||
func (c *CustomResponse) Continue() bool {
|
||||
for _, q := range []ActionCode{ActAccept, ActDiscard, ActReject, ActTempFail} {
|
||||
if c.code == byte(q) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NewResponse generates a new CustomResponse suitable for WritePacket
|
||||
func NewResponse(code byte, data []byte) *CustomResponse {
|
||||
return &CustomResponse{code, data}
|
||||
}
|
||||
|
||||
// NewResponseStr generates a new CustomResponse with string payload
|
||||
func NewResponseStr(code byte, data string) *CustomResponse {
|
||||
return NewResponse(code, []byte(data+null))
|
||||
}
|
||||
Reference in New Issue
Block a user