Skip to content

Commit ca39c8b

Browse files
authored
fix: input validation for RRSIG.Verify() (#1618)
* fix: input validation for RRSIG.verify() * fix: use labels.go/equal instead of strings.EqualFold for domain comparison
1 parent 8a00a2f commit ca39c8b

File tree

3 files changed

+141
-15
lines changed

3 files changed

+141
-15
lines changed

dnssec.go

+29-13
Original file line numberDiff line numberDiff line change
@@ -250,14 +250,6 @@ func (d *DS) ToCDS() *CDS {
250250
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
251251
// OrigTTL.
252252
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
253-
if k == nil {
254-
return ErrPrivKey
255-
}
256-
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
257-
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
258-
return ErrKey
259-
}
260-
261253
h0 := rrset[0].Header()
262254
rr.Hdr.Rrtype = TypeRRSIG
263255
rr.Hdr.Name = h0.Name
@@ -272,6 +264,18 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
272264
rr.Labels-- // wildcard, remove from label count
273265
}
274266

267+
return rr.signAsIs(k, rrset)
268+
}
269+
270+
func (rr *RRSIG) signAsIs(k crypto.Signer, rrset []RR) error {
271+
if k == nil {
272+
return ErrPrivKey
273+
}
274+
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
275+
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
276+
return ErrKey
277+
}
278+
275279
sigwire := new(rrsigWireFmt)
276280
sigwire.TypeCovered = rr.TypeCovered
277281
sigwire.Algorithm = rr.Algorithm
@@ -370,9 +374,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
370374
if rr.Algorithm != k.Algorithm {
371375
return ErrKey
372376
}
373-
if !strings.EqualFold(rr.SignerName, k.Hdr.Name) {
377+
378+
signerName := CanonicalName(rr.SignerName)
379+
if !equal(signerName, k.Hdr.Name) {
374380
return ErrKey
375381
}
382+
376383
if k.Protocol != 3 {
377384
return ErrKey
378385
}
@@ -384,9 +391,18 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
384391
}
385392

386393
// IsRRset checked that we have at least one RR and that the RRs in
387-
// the set have consistent type, class, and name. Also check that type and
388-
// class matches the RRSIG record.
389-
if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered {
394+
// the set have consistent type, class, and name. Also check that type,
395+
// class and name matches the RRSIG record.
396+
// Also checks RFC 4035 5.3.1 the number of labels in the RRset owner
397+
// name MUST be greater than or equal to the value in the RRSIG RR's Labels field.
398+
// RFC 4035 5.3.1 Signer's Name MUST be the name of the zone that [contains the RRset].
399+
// Since we don't have SOA info, checking suffix may be the best we can do...?
400+
if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class ||
401+
h0.Rrtype != rr.TypeCovered ||
402+
uint8(CountLabel(h0.Name)) < rr.Labels ||
403+
!equal(h0.Name, rr.Hdr.Name) ||
404+
!strings.HasSuffix(CanonicalName(h0.Name), signerName) {
405+
390406
return ErrRRset
391407
}
392408

@@ -400,7 +416,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
400416
sigwire.Expiration = rr.Expiration
401417
sigwire.Inception = rr.Inception
402418
sigwire.KeyTag = rr.KeyTag
403-
sigwire.SignerName = CanonicalName(rr.SignerName)
419+
sigwire.SignerName = signerName
404420
// Create the desired binary blob
405421
signeddata := make([]byte, DefaultMsgSize)
406422
n, err := packSigWire(sigwire, signeddata)

dnssec_test.go

+111
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,117 @@ func TestSignVerify(t *testing.T) {
155155
}
156156
}
157157

158+
// Test if RRSIG.Verify() conforms to RFC 4035 Section 5.3.1
159+
func TestShouldNotVerifyInvalidSig(t *testing.T) {
160+
// The RRSIG RR and the RRset MUST have the same owner name
161+
rrNameMismatch := getSoa()
162+
rrNameMismatch.Hdr.Name = "example.com."
163+
164+
// ... and the same class
165+
rrClassMismatch := getSoa()
166+
rrClassMismatch.Hdr.Class = ClassCHAOS
167+
168+
// The RRSIG RR's Type Covered field MUST equal the RRset's type.
169+
rrTypeMismatch := getSoa()
170+
rrTypeMismatch.Hdr.Rrtype = TypeA
171+
172+
// The number of labels in the RRset owner name MUST be greater than
173+
// or equal to the value in the RRSIG RR's Labels field.
174+
rrLabelLessThan := getSoa()
175+
rrLabelLessThan.Hdr.Name = "nl."
176+
177+
// Time checks are done in ValidityPeriod
178+
179+
// With this key
180+
key := new(DNSKEY)
181+
key.Hdr.Rrtype = TypeDNSKEY
182+
key.Hdr.Name = "miek.nl."
183+
key.Hdr.Class = ClassINET
184+
key.Hdr.Ttl = 14400
185+
key.Flags = 256
186+
key.Protocol = 3
187+
key.Algorithm = RSASHA256
188+
privkey, _ := key.Generate(512)
189+
190+
normalSoa := getSoa()
191+
192+
// Fill in the normal values of the Sig, before signing
193+
sig := new(RRSIG)
194+
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
195+
sig.TypeCovered = TypeSOA
196+
sig.Labels = uint8(CountLabel(normalSoa.Hdr.Name))
197+
sig.OrigTtl = normalSoa.Hdr.Ttl
198+
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
199+
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
200+
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
201+
sig.SignerName = key.Hdr.Name
202+
sig.Algorithm = RSASHA256
203+
204+
for i, rr := range []RR{rrNameMismatch, rrClassMismatch, rrTypeMismatch, rrLabelLessThan} {
205+
if i != 0 { // Just for the rrNameMismatch case, we need the name to mismatch
206+
sig := sig.copy().(*RRSIG)
207+
sig.SignerName = rr.Header().Name
208+
sig.Hdr.Name = rr.Header().Name
209+
key := key.copy().(*DNSKEY)
210+
key.Hdr.Name = rr.Header().Name
211+
}
212+
213+
if err := sig.signAsIs(privkey.(*rsa.PrivateKey), []RR{rr}); err != nil {
214+
t.Error("failure to sign the record:", err)
215+
continue
216+
}
217+
218+
if err := sig.Verify(key, []RR{rr}); err == nil {
219+
t.Error("should not validate: ", rr)
220+
continue
221+
} else {
222+
t.Logf("expected failure: %v for RR name %s, class %d, type %d, rrsig labels %d", err, rr.Header().Name, rr.Header().Class, rr.Header().Rrtype, CountLabel(rr.Header().Name))
223+
}
224+
}
225+
226+
// The RRSIG RR's Signer's Name field MUST be the name of the zone that contains the RRset.
227+
// The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner name,
228+
// algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset.
229+
sigMismatchName := sig.copy().(*RRSIG)
230+
sigMismatchName.SignerName = "example.com."
231+
soaMismatchName := getSoa()
232+
soaMismatchName.Hdr.Name = "example.com."
233+
keyMismatchName := key.copy().(*DNSKEY)
234+
keyMismatchName.Hdr.Name = "example.com."
235+
if err := sigMismatchName.signAsIs(privkey.(*rsa.PrivateKey), []RR{soaMismatchName}); err != nil {
236+
t.Error("failure to sign the record:", err)
237+
} else if err := sigMismatchName.Verify(keyMismatchName, []RR{soaMismatchName}); err == nil {
238+
t.Error("should not validate: ", soaMismatchName, ", RRSIG's signer's name does not match the owner name")
239+
} else {
240+
t.Logf("expected failure: %v for signer %s and owner %s", err, sigMismatchName.SignerName, sigMismatchName.Hdr.Name)
241+
}
242+
243+
sigMismatchAlgo := sig.copy().(*RRSIG)
244+
sigMismatchAlgo.Algorithm = RSASHA1
245+
sigMismatchKeyTag := sig.copy().(*RRSIG)
246+
sigMismatchKeyTag.KeyTag = 12345
247+
for _, sigMismatch := range []*RRSIG{sigMismatchAlgo, sigMismatchKeyTag} {
248+
if err := sigMismatch.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil {
249+
t.Error("failure to sign the record:", err)
250+
} else if err := sigMismatch.Verify(key, []RR{normalSoa}); err == nil {
251+
t.Error("should not validate: ", normalSoa)
252+
} else {
253+
t.Logf("expected failure: %v for signer %s algo %d keytag %d", err, sigMismatch.SignerName, sigMismatch.Algorithm, sigMismatch.KeyTag)
254+
}
255+
}
256+
257+
// The matching DNSKEY RR MUST have the Zone Flag bit (DNSKEY RDATA Flag bit 7) set.
258+
keyZoneBitWrong := key.copy().(*DNSKEY)
259+
keyZoneBitWrong.Flags = key.Flags &^ ZONE
260+
if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{normalSoa}); err != nil {
261+
t.Error("failure to sign the record:", err)
262+
} else if err := sig.Verify(keyZoneBitWrong, []RR{normalSoa}); err == nil {
263+
t.Error("should not validate: ", normalSoa)
264+
} else {
265+
t.Logf("expected failure: %v for key flags %d", err, keyZoneBitWrong.Flags)
266+
}
267+
}
268+
158269
func Test65534(t *testing.T) {
159270
t6 := new(RFC3597)
160271
t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}

sig0.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"crypto/rsa"
88
"encoding/binary"
99
"math/big"
10-
"strings"
1110
"time"
1211
)
1312

@@ -151,7 +150,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
151150
}
152151
// If key has come from the DNS name compression might
153152
// have mangled the case of the name
154-
if !strings.EqualFold(signername, k.Header().Name) {
153+
if !equal(signername, k.Header().Name) {
155154
return &Error{err: "signer name doesn't match key name"}
156155
}
157156
sigend := offset

0 commit comments

Comments
 (0)