package rfc2136 import ( "fmt" "strings" "github.com/miekg/dns" ) // checkTSIG verifies that the incoming UPDATE message is properly signed // with a TSIG key we know about. The actual signature math has already // been done by the underlying dns.Server (because setup.go registered // our keys in dnsserver.Config.TsigSecret); this function just inspects // the result and the key identity. // // Behavior matrix: // // No TSIG keys configured → updates are unauthenticated. Caller may // still allow if it deems the network safe; // we conservatively reject (REFUSED) since // the practical use case (Caddy) always // signs. // TSIG keys configured but message has no TSIG → reject. // TSIG present, key name not in our map → reject. // TSIG present, signature failed at dns.Server → reject (TsigStatus()). // All good → nil. func (p *RFC2136) checkTSIG(w dns.ResponseWriter, r *dns.Msg) error { tsig := r.IsTsig() if len(p.TSIGKeys) == 0 { return fmt.Errorf("no TSIG keys configured; refusing all UPDATEs as a safety default") } if tsig == nil { return fmt.Errorf("TSIG required but not present") } keyName := strings.ToLower(tsig.Hdr.Name) if !strings.HasSuffix(keyName, ".") { keyName += "." } key, known := p.TSIGKeys[keyName] if !known { return fmt.Errorf("unknown TSIG key %q", keyName) } // Algorithm pinning: the incoming TSIG can in theory use any // algorithm miekg/dns supports, but we only honour the one declared // in Corefile. Rejecting algorithm-downgrade attempts is a small // but important hardening — without this, an attacker who somehow // got a key could downgrade to HMAC-MD5 (which we don't even // configure but miekg/dns understands). if !strings.EqualFold(tsig.Algorithm, key.Algorithm) { return fmt.Errorf("TSIG algorithm mismatch: incoming=%s expected=%s", tsig.Algorithm, key.Algorithm) } // The underlying dns.Server verifies the TSIG MAC for us when it // has the secret in its TsigSecret map (which setup.go wires up). // A nil from TsigStatus means verification succeeded; any non-nil // error means the signature was invalid, the time was outside the // fudge window, or some other auth failure. if status := w.TsigStatus(); status != nil { return fmt.Errorf("TSIG verification failed for key %q: %w", keyName, status) } return nil }