dns/nsecx.go

263 lines
7.0 KiB
Go
Raw Normal View History

2011-01-28 01:52:58 +11:00
package dns
2011-03-08 07:56:36 +11:00
import (
2011-12-10 07:45:57 +11:00
"crypto/sha1"
2011-03-08 08:47:20 +11:00
"hash"
"io"
2011-03-08 08:47:20 +11:00
"strings"
2011-03-08 07:56:36 +11:00
)
2012-01-22 21:33:51 +11:00
const (
_ = iota
2012-08-20 17:39:10 +10:00
_NSEC3_NXDOMAIN
_NSEC3_NODATA
2012-01-22 21:33:51 +11:00
)
2012-06-06 23:27:35 +10:00
// A Denialer is a record that performs denial
// of existence in DNSSEC. Currently there are
// two types NSEC and NSEC3.
type Denialer interface {
// HashNames hashes the owner and next domain name according
// to the hashing set in the record. For NSEC it is the identity function.
// The string domain is appended to the ownername in case of NSEC3
HashNames(domain string)
// Match checks if domain matches the (hashed) owner of name of the record.
Match(domain string) bool
// Cover checks if domain is covered by the NSEC(3) record
Cover(domain string) bool
// MatchType checks if the type is present in the bitmap
MatchType(rrtype uint16) bool
}
2011-03-08 08:47:20 +11:00
type saltWireFmt struct {
Salt string `dns:"size-hex"`
2011-03-08 08:47:20 +11:00
}
2011-01-28 01:52:58 +11:00
2012-01-17 08:08:02 +11:00
// HashName hashes a string (label) according to RFC5155. It returns the hashed string.
2012-01-22 20:52:06 +11:00
func HashName(label string, ha uint8, iter uint16, salt string) string {
2011-03-08 08:47:20 +11:00
saltwire := new(saltWireFmt)
saltwire.Salt = salt
wire := make([]byte, DefaultMsgSize)
n, ok := PackStruct(saltwire, wire, 0)
2011-03-08 08:47:20 +11:00
if !ok {
return ""
}
wire = wire[:n]
2011-03-10 00:27:41 +11:00
name := make([]byte, 255)
off, ok1 := PackDomainName(strings.ToLower(label), name, 0, nil, false)
2011-03-08 08:47:20 +11:00
if !ok1 {
return ""
}
2011-03-10 00:27:41 +11:00
name = name[:off]
2011-03-08 08:47:20 +11:00
var s hash.Hash
switch ha {
2011-07-09 01:27:44 +10:00
case SHA1:
2011-03-08 08:47:20 +11:00
s = sha1.New()
2011-03-24 19:24:49 +11:00
default:
return ""
2011-03-08 08:47:20 +11:00
}
// k = 0
2011-03-10 00:27:41 +11:00
name = append(name, wire...)
2011-12-17 05:42:30 +11:00
io.WriteString(s, string(name))
nsec3 := s.Sum(nil)
2011-03-24 19:24:49 +11:00
// k > 0
2012-01-22 20:52:06 +11:00
for k := uint16(0); k < iter; k++ {
2011-03-24 19:24:49 +11:00
s.Reset()
nsec3 = append(nsec3, wire...)
2011-12-17 05:42:30 +11:00
io.WriteString(s, string(nsec3))
2011-12-17 01:12:01 +11:00
nsec3 = s.Sum(nil)
2011-03-24 19:24:49 +11:00
}
2011-03-08 08:47:20 +11:00
return unpackBase32(nsec3)
2011-03-08 07:56:36 +11:00
}
2011-03-10 04:54:55 +11:00
2012-06-06 23:27:35 +10:00
// Implement the HashNames method of Denialer
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC3) HashNames(domain string) {
rr.Header().Name = strings.ToLower(HashName(rr.Header().Name, rr.Hash, rr.Iterations, rr.Salt)) + "." + domain
rr.NextDomain = HashName(rr.NextDomain, rr.Hash, rr.Iterations, rr.Salt)
2012-01-22 20:52:06 +11:00
}
2012-06-06 23:27:35 +10:00
// Implement the Match method of Denialer
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC3) Match(domain string) bool {
return strings.ToUpper(SplitLabels(rr.Header().Name)[0]) == strings.ToUpper(HashName(domain, rr.Hash, rr.Iterations, rr.Salt))
2012-01-22 21:33:51 +11:00
}
2012-06-06 23:27:35 +10:00
// Implement the Match method of Denialer
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC) Match(domain string) bool {
return strings.ToUpper(rr.Header().Name) == strings.ToUpper(domain)
2012-06-06 23:27:35 +10:00
}
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC3) MatchType(rrtype uint16) bool {
for _, t := range rr.TypeBitMap {
2012-06-06 23:27:35 +10:00
if t == rrtype {
return true
}
if t > rrtype {
return false
}
}
return false
}
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC) MatchType(rrtype uint16) bool {
for _, t := range rr.TypeBitMap {
2012-06-06 23:27:35 +10:00
if t == rrtype {
return true
}
if t > rrtype {
return false
}
}
return false
}
2012-06-06 15:36:45 +10:00
// Cover checks if domain is covered by the NSEC3 record. Domain must be given in plain text (i.e. not hashed)
// TODO(mg): this doesn't loop around
// TODO(mg): make a CoverHashed variant?
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC3) Cover(domain string) bool {
hashdom := strings.ToUpper(HashName(domain, rr.Hash, rr.Iterations, rr.Salt))
nextdom := strings.ToUpper(rr.NextDomain)
owner := strings.ToUpper(SplitLabels(rr.Header().Name)[0]) // The hashed part
apex := strings.ToUpper(HashName(strings.Join(SplitLabels(rr.Header().Name)[1:], "."), rr.Hash, rr.Iterations, rr.Salt)) + "." // The name of the zone
// if nextdomain equals the apex, it is considered The End. So in that case hashdom is always less then nextdomain
if hashdom > owner && nextdom == apex {
return true
}
if hashdom > owner && hashdom <= nextdom {
return true
}
return false
2011-03-10 04:54:55 +11:00
}
2011-09-16 04:13:21 +10:00
2012-03-09 07:16:51 +11:00
// Cover checks if domain is covered by the NSEC record. Domain must be given in plain text.
2012-09-02 19:45:29 +10:00
func (rr *RR_NSEC) Cover(domain string) bool {
2012-03-09 07:16:51 +11:00
return false
}
2012-01-18 05:16:58 +11:00
// NsecVerify verifies an denial of existence response with NSECs
2011-09-16 04:13:21 +10:00
// NsecVerify returns nil when the NSECs in the message contain
2012-01-18 04:48:40 +11:00
// the correct proof. This function does not validates the NSECs.
2011-11-03 09:06:54 +11:00
func (m *Msg) NsecVerify(q Question) error {
2011-09-16 04:13:21 +10:00
2011-11-03 09:06:54 +11:00
return nil
2011-09-16 04:13:21 +10:00
}
2012-01-18 05:16:58 +11:00
// Nsec3Verify verifies an denial of existence response with NSEC3s.
// This function does not validate the NSEC3s.
2012-01-22 21:33:51 +11:00
func (m *Msg) Nsec3Verify(q Question) (int, error) {
2012-01-20 05:48:09 +11:00
var (
nsec3 []*RR_NSEC3
ncdenied = false // next closer denied
sodenied = false // source of synthesis denied
2012-01-22 21:33:51 +11:00
ce = "" // closest encloser
nc = "" // next closer
so = "" // source of synthesis
2012-01-20 05:48:09 +11:00
)
2012-01-18 05:16:58 +11:00
if len(m.Answer) > 0 && len(m.Ns) > 0 {
// Wildcard expansion
// Closest encloser inferred from SIG in authority and qname
2012-01-20 05:48:09 +11:00
// println("EXPANDED WILDCARD PROOF or DNAME CNAME")
// println("NODATA")
2012-01-18 05:16:58 +11:00
// I need to check the type bitmap
// wildcard bit not set?
// MM: No need to check the wildcard bit here:
// This response has only 1 NSEC4 and it does not match
// the closest encloser (it covers next closer).
}
if len(m.Answer) == 0 && len(m.Ns) > 0 {
2012-01-22 21:33:51 +11:00
// Maybe an NXDOMAIN or NODATA, we only know when we check
2012-01-20 05:48:09 +11:00
for _, n := range m.Ns {
if n.Header().Rrtype == TypeNSEC3 {
nsec3 = append(nsec3, n.(*RR_NSEC3))
2012-01-18 05:16:58 +11:00
}
}
2012-01-20 05:48:09 +11:00
if len(nsec3) == 0 {
2012-01-22 21:33:51 +11:00
return 0, ErrDenialNsec3
2012-01-20 05:48:09 +11:00
}
2012-01-18 05:16:58 +11:00
2012-01-20 05:48:09 +11:00
lastchopped := ""
labels := SplitLabels(q.Name)
2012-01-18 05:16:58 +11:00
2012-01-20 05:48:09 +11:00
// Find the closest encloser and create the next closer
for _, nsec := range nsec3 {
candidate := ""
for i := len(labels) - 1; i >= 0; i-- {
candidate = labels[i] + "." + candidate
2012-01-22 20:52:06 +11:00
if nsec.Match(candidate) {
2012-01-20 05:48:09 +11:00
ce = candidate
2012-01-18 05:16:58 +11:00
}
2012-01-20 05:48:09 +11:00
lastchopped = labels[i]
2012-01-18 05:16:58 +11:00
}
}
2012-01-20 05:48:09 +11:00
if ce == "" { // what about root label?
2012-01-22 21:33:51 +11:00
return 0, ErrDenialCe
2012-01-20 05:48:09 +11:00
}
nc = lastchopped + "." + ce
so = "*." + ce
// Check if the next closer is covered and thus denied
for _, nsec := range nsec3 {
2012-01-22 21:33:51 +11:00
if nsec.Cover(nc) {
2012-01-20 05:48:09 +11:00
ncdenied = true
2012-01-20 22:24:20 +11:00
break
2012-01-18 05:16:58 +11:00
}
2012-01-20 05:48:09 +11:00
}
if !ncdenied {
if m.MsgHdr.Rcode == RcodeNameError {
// For NXDOMAIN this is a problem
return 0, ErrDenialNc // add next closer name here
}
2012-01-22 21:33:51 +11:00
goto NoData
2012-01-18 05:16:58 +11:00
}
2011-09-16 04:13:21 +10:00
// Check if the source of synthesis is covered and thus also denied
2012-01-20 05:48:09 +11:00
for _, nsec := range nsec3 {
2012-01-22 21:33:51 +11:00
if nsec.Cover(so) {
2012-01-20 05:48:09 +11:00
sodenied = true
2012-01-20 22:24:20 +11:00
break
2012-01-18 05:16:58 +11:00
}
}
2012-01-20 05:48:09 +11:00
if !sodenied {
2012-01-22 21:33:51 +11:00
return 0, ErrDenialSo
2012-01-20 05:48:09 +11:00
}
// The message headers claims something different!
if m.MsgHdr.Rcode != RcodeNameError {
return 0, ErrDenialHdr
}
2012-08-20 17:39:10 +10:00
return _NSEC3_NXDOMAIN, nil
2012-01-18 05:16:58 +11:00
}
2012-01-22 21:33:51 +11:00
return 0, nil
NoData:
2012-02-25 09:43:34 +11:00
// For NODATA we need to to check if the matching nsec3 has to correct type bit map
// And we need to check that the wildcard does NOT exist
for _, nsec := range nsec3 {
if nsec.Cover(so) {
sodenied = true
break
}
}
if sodenied {
// Whoa, the closest encloser is denied, but there does exist
// a wildcard a that level. That's not good
return 0, ErrDenialWc
}
2012-01-22 21:33:51 +11:00
// The closest encloser MUST be the query name
for _, nsec := range nsec3 {
if nsec.Match(nc) {
// This nsec3 must NOT have the type bitmap set of the qtype. If it does have it, return an error
for _, t := range nsec.TypeBitMap {
if t == q.Qtype {
return 0, ErrDenialBit
}
}
}
}
if m.MsgHdr.Rcode == RcodeNameError {
return 0, ErrDenialHdr
}
2012-08-20 17:39:10 +10:00
return _NSEC3_NODATA, nil
2011-09-16 04:13:21 +10:00
}