GitHub Security Advisory: GHSA-vp29-5652-4fw9
Package: github.com/coredns/coredns (GO)
Severity: HIGH (CVSS 7.5)
CWE: CWE-287: Improper Authentication
Vulnerable versions: < 1.14.3
Patched in: 1.14.3

Summary: CoreDNS has TSIG authentication bypass on gRPC and QUIC transports

Details:
### Summary

The gRPC, QUIC, DoH, and DoH3 transports in CoreDNS incorrectly handle TSIG authentication.

For gRPC and QUIC, CoreDNS checks whether the TSIG key name exists in the config, but does not actually verify the TSIG HMAC. If the key name matches, `tsigStatus` remains nil and the tsig plugin treats the request as "verified".

For DoH and DoH3, the issue is worse: TSIG is not verified at all. The DoH response writer has `TsigStatus()` hardcoded to return nil, so any request containing a TSIG record is treated as authenticated, even if the key name is invalid and the MAC is garbage.

As a result, attackers may bypass TSIG authentication on affected transports and access TSIG-protected functionality such as AXFR/IXFR zone transfers, dynamic updates, or other TSIG-gated plugin behavior.

### Details

In `server_grpc.go` and `server_quic.go`, the TSIG handling checks whether the TSIG key name exists, but does not call `dns.TsigVerify()`.

Relevant code before fix:

```go
if tsig := msg.IsTsig(); tsig != nil {
    if s.tsigSecret == nil {
        w.tsigStatus = dns.ErrSecret
    } else if _, ok := s.tsigSecret[tsig.Hdr.Name]; !ok {
        w.tsigStatus = dns.ErrSecret
    }
    // key found -> nothing happens -> tsigStatus stays nil -> "verified"
}
```

This means that for gRPC and QUIC, a request with a known TSIG key name but an invalid MAC is accepted as authenticated.

PRs #7943 and #7947 partially addressed this area by adding key name checks for gRPC and QUIC, but did not add HMAC verification.

The DoH and DoH3 paths have an even weaker failure mode. In `https.go`, `DoHWriter.TsigStatus()` returned nil unconditionally:

```go
func (d *DoHWriter) TsigStatus() error {
    return nil
}
```

In `server_https.go`, the incoming DNS message is unpacked from the HTTP request and passed directly into `ServeDNS()` without checking `msg.IsTsig()`, without looking up the TSIG key name, and without calling `dns.TsigVerify()`.

The same pattern exists in the DoH3 path in `server_https3.go`.

The effective DoH/DoH3 flow before the fix was:

1. HTTP or HTTP/3 request arrives.
2. DNS message is unpacked from the request.
3. A `DoHWriter` is created.
4. The message is passed to `ServeDNS()`.
5. The tsig plugin checks `w.TsigStatus()`.
6. `TsigStatus()` returns nil.
7. nil is interpreted as successful TSIG verification.

This means that for DoH and DoH3, CoreDNS did not even require a valid TSIG key name. Any TSIG record was enough to satisfy the tsig plugin, regardless of key name or MAC contents.

### PoC

Setup: built CoreDNS from master at commit `12d9457` and also verified against the v1.14.2 release binary. Configured a single test zone with 9 records and `tsig { require all }`.

Listeners used the same TSIG configuration and key:

- TCP on port 1053, using the normal `dns.Server` path where TSIG HMAC verification works correctly
- gRPC on port 1443, using manual TSIG handling
- DoH on port 8443
- DoH3 with the same TSIG configuration

#### gRPC / Q