183 lines
4.3 KiB
Go
183 lines
4.3 KiB
Go
// Copyright 2011 Miek Gieben. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package dns
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"hash"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
type saltWireFmt struct {
|
|
Salt string `dns:"size-hex"`
|
|
}
|
|
|
|
// HashName hashes a string (label) according to RFC 5155. It returns the hashed string.
|
|
func HashName(label string, ha uint8, iter uint16, salt string) string {
|
|
saltwire := new(saltWireFmt)
|
|
saltwire.Salt = salt
|
|
wire := make([]byte, DefaultMsgSize)
|
|
n, err := PackStruct(saltwire, wire, 0)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
wire = wire[:n]
|
|
name := make([]byte, 255)
|
|
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
name = name[:off]
|
|
var s hash.Hash
|
|
switch ha {
|
|
case SHA1:
|
|
s = sha1.New()
|
|
default:
|
|
return ""
|
|
}
|
|
|
|
// k = 0
|
|
name = append(name, wire...)
|
|
io.WriteString(s, string(name))
|
|
nsec3 := s.Sum(nil)
|
|
// k > 0
|
|
for k := uint16(0); k < iter; k++ {
|
|
s.Reset()
|
|
nsec3 = append(nsec3, wire...)
|
|
io.WriteString(s, string(nsec3))
|
|
nsec3 = s.Sum(nil)
|
|
}
|
|
return unpackBase32(nsec3)
|
|
}
|
|
|
|
type Denialer interface {
|
|
// Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3.
|
|
Cover(name string) bool
|
|
// Match will check if the ownername matches the (unhashed) name for this NSEC3 or NSEC3.
|
|
Match(name string) bool
|
|
}
|
|
|
|
// Cover implements the Denialer interface.
|
|
func (rr *NSEC) Cover(name string) bool {
|
|
return true
|
|
}
|
|
|
|
// Match implements the Denialer interface.
|
|
func (rr *NSEC) Match(name string) bool {
|
|
return true
|
|
}
|
|
|
|
// Cover implements the Denialer interface.
|
|
func (rr *NSEC3) Cover(name string) bool {
|
|
// FIXME(miek): check if the zones match
|
|
// FIXME(miek): check if we're not dealing with parent nsec3
|
|
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
|
labels := Split(rr.Hdr.Name)
|
|
if len(labels) < 2 {
|
|
return false
|
|
}
|
|
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the dot
|
|
if hash == rr.NextDomain {
|
|
return false // empty interval
|
|
}
|
|
if hash > rr.NextDomain { // last name, points to apex
|
|
// hname > hash
|
|
// hname > rr.NextDomain
|
|
// TODO(miek)
|
|
}
|
|
if hname <= hash {
|
|
return false
|
|
}
|
|
if hname >= rr.NextDomain {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Match implements the Denialer interface.
|
|
func (rr *NSEC3) Match(name string) bool {
|
|
// FIXME(miek): Check if we are in the same zone
|
|
hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
|
labels := Split(rr.Hdr.Name)
|
|
if len(labels) < 2 {
|
|
return false
|
|
}
|
|
hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the .
|
|
if hash == hname {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// VerifyNameError takes a slice of NSEC or NSEC3 RR, the qname and the qtype and tries
|
|
// to verify the authenticated denial of existence. If nil is returned the proof
|
|
// succeeded otherwise the error will indicated what was wrong.
|
|
func VerifyNameError(nsecx []RR, qname string, qtype uint16) error {
|
|
// TODO(miek): wildcard expanded reply
|
|
nsec3 := 0
|
|
nsec := 0
|
|
for i := 0; i < len(nsecx); i++ {
|
|
if _, ok := nsecx[0].(*NSEC3); ok {
|
|
nsec3++
|
|
}
|
|
if _, ok := nsecx[0].(*NSEC); ok {
|
|
nsec++
|
|
}
|
|
}
|
|
if nsec3 == len(nsecx) {
|
|
return verifyNameError3(nsecx, qname, qtype)
|
|
}
|
|
if nsec == len(nsecx) {
|
|
return verifyNameError(nsecx, qname, qtype)
|
|
}
|
|
return ErrSig // ErrNotRRset?
|
|
}
|
|
|
|
// NSEC3 Helper
|
|
func verifyNameError3(nsec3 []RR, qname string, qtype uint16) error {
|
|
indx := Split(qname)
|
|
ce := "" // Closest Encloser
|
|
nc := "" // Next Closer
|
|
wc := "" // Source of Synthesis (wildcard)
|
|
ClosestEncloser:
|
|
for i := 0; i < len(indx); i++ {
|
|
for j := 0; j < len(nsec3); j++ {
|
|
if nsec3[j].(*NSEC3).Match(qname[indx[i]:]) {
|
|
ce = qname[indx[i]:]
|
|
wc = "*." + ce
|
|
if i == 0 {
|
|
nc = qname
|
|
} else {
|
|
nc = qname[indx[i-1]:]
|
|
}
|
|
break ClosestEncloser
|
|
}
|
|
}
|
|
}
|
|
if ce == "" {
|
|
return ErrSig // ErrNoMatchingNSEC3
|
|
}
|
|
covered := 0 // Both nc and wc must be covered
|
|
for i := 0; i < len(nsec3); i++ {
|
|
if nsec3[i].(*NSEC3).Cover(nc) {
|
|
covered++
|
|
}
|
|
if nsec3[i].(*NSEC3).Cover(wc) {
|
|
covered++
|
|
}
|
|
}
|
|
if covered != 2 {
|
|
return ErrSig
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NSEC Helper
|
|
func verifyNameError(nsecx []RR, qname string, qtype uint16) error { return nil }
|
|
|
|
// VerifyNoData
|
|
func VerifyNoData(nsecx []RR, qname string, qtype uint16) error { return nil}
|