Add nsec3 NXDOMAIN proof
This commit is contained in:
parent
f1097691bb
commit
ea789b6f23
|
@ -145,9 +145,7 @@ forever:
|
|||
r.Reply = shortMsg(r.Reply)
|
||||
}
|
||||
if *check {
|
||||
// Get the signatures from the message
|
||||
// Get the keys mentioned from the internet
|
||||
// Validate it
|
||||
r.Reply.Nsec3Verify(r.Reply.Question[0])
|
||||
|
||||
}
|
||||
fmt.Printf("%v", r.Reply)
|
||||
|
|
57
msg.go
57
msg.go
|
@ -26,32 +26,35 @@ import (
|
|||
const maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer
|
||||
|
||||
var (
|
||||
ErrUnpack error = &Error{Err: "unpacking failed"}
|
||||
ErrPack error = &Error{Err: "packing failed"}
|
||||
ErrId error = &Error{Err: "id mismatch"}
|
||||
ErrShortRead error = &Error{Err: "short read"}
|
||||
ErrConn error = &Error{Err: "conn holds both UDP and TCP connection"}
|
||||
ErrConnEmpty error = &Error{Err: "conn has no connection"}
|
||||
ErrServ error = &Error{Err: "no servers could be reached"}
|
||||
ErrKey error = &Error{Err: "bad key"}
|
||||
ErrPrivKey error = &Error{Err: "bad private key"}
|
||||
ErrKeySize error = &Error{Err: "bad key size"}
|
||||
ErrKeyAlg error = &Error{Err: "bad key algorithm"}
|
||||
ErrAlg error = &Error{Err: "bad algorithm"}
|
||||
ErrTime error = &Error{Err: "bad time"}
|
||||
ErrNoSig error = &Error{Err: "no signature found"}
|
||||
ErrSig error = &Error{Err: "bad signature"}
|
||||
ErrSecret error = &Error{Err: "no secret defined"}
|
||||
ErrSigGen error = &Error{Err: "bad signature generation"}
|
||||
ErrAuth error = &Error{Err: "bad authentication"}
|
||||
ErrXfrSoa error = &Error{Err: "no SOA seen"}
|
||||
ErrXfrLast error = &Error{Err: "last SOA"}
|
||||
ErrXfrType error = &Error{Err: "no ixfr, nor axfr"}
|
||||
ErrHandle error = &Error{Err: "handle is nil"}
|
||||
ErrChan error = &Error{Err: "channel is nil"}
|
||||
ErrName error = &Error{Err: "type not found for name"}
|
||||
ErrRRset error = &Error{Err: "invalid rrset"}
|
||||
ErrNoNSec3 error = &Error{Err: "no NSEC3 records"}
|
||||
ErrUnpack error = &Error{Err: "unpacking failed"}
|
||||
ErrPack error = &Error{Err: "packing failed"}
|
||||
ErrId error = &Error{Err: "id mismatch"}
|
||||
ErrShortRead error = &Error{Err: "short read"}
|
||||
ErrConn error = &Error{Err: "conn holds both UDP and TCP connection"}
|
||||
ErrConnEmpty error = &Error{Err: "conn has no connection"}
|
||||
ErrServ error = &Error{Err: "no servers could be reached"}
|
||||
ErrKey error = &Error{Err: "bad key"}
|
||||
ErrPrivKey error = &Error{Err: "bad private key"}
|
||||
ErrKeySize error = &Error{Err: "bad key size"}
|
||||
ErrKeyAlg error = &Error{Err: "bad key algorithm"}
|
||||
ErrAlg error = &Error{Err: "bad algorithm"}
|
||||
ErrTime error = &Error{Err: "bad time"}
|
||||
ErrNoSig error = &Error{Err: "no signature found"}
|
||||
ErrSig error = &Error{Err: "bad signature"}
|
||||
ErrSecret error = &Error{Err: "no secret defined"}
|
||||
ErrSigGen error = &Error{Err: "bad signature generation"}
|
||||
ErrAuth error = &Error{Err: "bad authentication"}
|
||||
ErrXfrSoa error = &Error{Err: "no SOA seen"}
|
||||
ErrXfrLast error = &Error{Err: "last SOA"}
|
||||
ErrXfrType error = &Error{Err: "no ixfr, nor axfr"}
|
||||
ErrHandle error = &Error{Err: "handle is nil"}
|
||||
ErrChan error = &Error{Err: "channel is nil"}
|
||||
ErrName error = &Error{Err: "type not found for name"}
|
||||
ErrRRset error = &Error{Err: "invalid rrset"}
|
||||
ErrDenialNsec3 error = &Error{Err: "no NSEC3 records"}
|
||||
ErrDenialCe error = &Error{Err: "no matching closest encloser found"}
|
||||
ErrDenialNc error = &Error{Err: "no covering NSEC3 found for next closer"}
|
||||
ErrDenialSo error = &Error{Err: "no covering NSEC3 found for source of synthesis"}
|
||||
)
|
||||
|
||||
// A manually-unpacked version of (id, bits).
|
||||
|
@ -645,7 +648,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
|
|||
case "RR_NSEC":
|
||||
endrr = off + (rdlength - (len(val.FieldByName("NextDomain").String()) + 1))
|
||||
case "RR_NSEC3":
|
||||
// NextDomain is always 20 for NextDomain
|
||||
// NextDomain is always 20 for NextDomain
|
||||
endrr = off + (rdlength - (20 + 6 + len(val.FieldByName("Salt").String())/2))
|
||||
}
|
||||
|
||||
|
|
176
nsec3.go
176
nsec3.go
|
@ -68,142 +68,92 @@ func (m *Msg) NsecVerify(q Question) error {
|
|||
// Nsec3Verify verifies an denial of existence response with NSEC3s.
|
||||
// This function does not validate the NSEC3s.
|
||||
func (m *Msg) Nsec3Verify(q Question) error {
|
||||
/*
|
||||
var nsec3 []*RR_NSEC3
|
||||
var (
|
||||
nsec3 []*RR_NSEC3
|
||||
ncdenied = false // next closer denied
|
||||
sodenied = false // source of synthesis denied
|
||||
)
|
||||
if len(m.Answer) > 0 && len(m.Ns) > 0 {
|
||||
// Wildcard expansion
|
||||
// Closest encloser inferred from SIG in authority and qname
|
||||
println("EXPANDED WILDCARD PROOF or DNAME CNAME")
|
||||
println("NODATA")
|
||||
// println("EXPANDED WILDCARD PROOF or DNAME CNAME")
|
||||
// println("NODATA")
|
||||
// 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 {
|
||||
// Maybe an NXDOMAIN, we only know when we check
|
||||
for _, n := range m.Ns {
|
||||
if n.Rrtype == TypeNSEC3 {
|
||||
nsec3 = append(nsec3, n.(*RR_NSEC3))
|
||||
}
|
||||
}
|
||||
if len(nsec3) == 0 {
|
||||
return ErrNoNsec3
|
||||
for _, n := range m.Ns {
|
||||
if n.Header().Rrtype == TypeNSEC3 {
|
||||
nsec3 = append(nsec3, n.(*RR_NSEC3))
|
||||
}
|
||||
}
|
||||
if len(nsec3) == 0 {
|
||||
return ErrDenialNsec3
|
||||
}
|
||||
|
||||
hash := nsec3[0].(*RR_NSEC3).Hash
|
||||
iter := nsec3[0].(*RR_NSEC3).Iterations
|
||||
salt := nsec3[0].(*RR_NSEC3).Salt
|
||||
ce := "goed.fout."
|
||||
ClosestEncloser:
|
||||
hash := int(nsec3[0].Hash)
|
||||
iter := int(nsec3[0].Iterations)
|
||||
salt := nsec3[0].Salt
|
||||
ce := "" // closest encloser
|
||||
nc := "" // next closer
|
||||
so := "" // source of synthesis
|
||||
lastchopped := ""
|
||||
labels := SplitLabels(q.Name)
|
||||
|
||||
// Find the closest encloser and create the next closer
|
||||
for _, nsec := range nsec3 {
|
||||
for _, candidate := range LabelSlice(q.Name) {
|
||||
println("H:", HashName(ce1, algo, iter, salt)+suffix)
|
||||
println("N:", strings.ToUpper(nsec.Header().Name))
|
||||
if HashName(ce1, algo, iter, salt)+suffix == strings.ToUpper(nsec.Header().Name) {
|
||||
ce = ce1
|
||||
break ClosestEncloser
|
||||
candidate := ""
|
||||
firstlab := strings.ToUpper(SplitLabels(nsec.Header().Name)[0])
|
||||
for i := len(labels) - 1; i >= 0; i-- {
|
||||
candidate = labels[i] + "." + candidate
|
||||
if HashName(candidate, hash, iter, salt) == firstlab {
|
||||
ce = candidate
|
||||
}
|
||||
lastchopped = labels[i]
|
||||
}
|
||||
}
|
||||
if ce == "goed.fout." {
|
||||
// If we didn't find the closest here, we have a NODATA wilcard response
|
||||
println("CE NIET GEVONDEN")
|
||||
println(" (WILDCARD) NODATA RESPONSE")
|
||||
// chop the qname, append the wildcard label, and see it we have a match
|
||||
// Zijn we nog wel in de zone bezig als we deze antwoord hebben
|
||||
// dat moeten we toch wel controleren TODO(MG)
|
||||
// MM: source-of-synthesis (source) = *.closest-encloser (ce)
|
||||
// 1. ce is an ancestor of QNAME of which source is matched by an
|
||||
// NSEC4 RR present in the response.
|
||||
// 2. The name one label longer than ce (but still an ancestor of --
|
||||
// or equal to -- QNAME) is covered by an NSEC4 RR present in the
|
||||
// response.
|
||||
// 3. Are the NSEC4 RRs from the proper zone?
|
||||
// The NSEC4 that matches the wildcard RR is:
|
||||
// Check that the signer field in the RRSIG on both NSEC4 RRs
|
||||
// is the same. If so, both NSEC4 RRs are from the same zone.
|
||||
if ce == "" { // what about root label?
|
||||
return ErrDenialCe
|
||||
}
|
||||
nc = lastchopped + "." + ce
|
||||
so = "*." + ce
|
||||
|
||||
Synthesis:
|
||||
for _, nsec := range nsec4 {
|
||||
for _, ce1 := range LabelSlice(q.Name) {
|
||||
source := "*." + ce1
|
||||
if ce1 == "." {
|
||||
source = "*."
|
||||
|
||||
}
|
||||
println(source, ":", HashName(source, algo, iter, salt))
|
||||
println(" : ", strings.ToUpper(nsec.Header().Name))
|
||||
if HashName(source, algo, iter, salt)+suffix == strings.ToUpper(nsec.Header().Name) {
|
||||
ce = ce1
|
||||
break Synthesis
|
||||
}
|
||||
}
|
||||
}
|
||||
println("Source of synthesis found, CE = ", ce)
|
||||
// Als niet gevonden, shit hits the fan?!
|
||||
// MM: je hebt nog niet de gewone NODATA geprobeerd...
|
||||
// need nsec that matches the qname directly
|
||||
// if HashName(q.Name, algo, iter, salt)+suffix == strings.ToUpper(nsec.Header().Name)
|
||||
|
||||
|
||||
if ce == "goed.fout." {
|
||||
println("Source of synth not found")
|
||||
// Check if the next closer is covered and thus denied
|
||||
for _, nsec := range nsec3 {
|
||||
firstlab := strings.ToUpper(SplitLabels(nsec.Header().Name)[0])
|
||||
nextdom := strings.ToUpper(nsec.NextDomain)
|
||||
hashednc := HashName(nc, hash, iter, salt)
|
||||
if hashednc > firstlab && hashednc < nextdom {
|
||||
ncdenied = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if q.Name == ce -> Check nodata, wildcard flag off
|
||||
if strings.ToUpper(q.Name) == strings.ToUpper(ce) {
|
||||
println("WE HAVE TO DO A NODATA PROOF 2")
|
||||
for _, nsec := range nsec4 {
|
||||
println(HashName(ce, algo, iter, salt)+suffix, strings.ToUpper(nsec.Header().Name))
|
||||
if HashName(ce, algo, iter, salt)+suffix == strings.ToUpper(nsec.Header().Name) {
|
||||
fmt.Printf("We should not have the type %s (%d)? %v\n", Rr_str[q.Qtype], q.Qtype, !bitmap(nsec.(*RR_NSEC4), q.Qtype))
|
||||
fmt.Printf(" we have: %v\n", nsec.(*RR_NSEC4).TypeBitMap)
|
||||
if !bitmap(nsec.(*RR_NSEC4), q.Qtype) {
|
||||
println("NODATA IS PROVEN, IF NSEC4S ARE VALID")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
println("CHECK TYPE BITMAP 2")
|
||||
return nil
|
||||
if !ncdenied {
|
||||
return ErrDenialNc
|
||||
}
|
||||
|
||||
nc := NextCloser(q.Name, ce)
|
||||
|
||||
println("Clostest encloser found:", ce, HashName(ce, algo, iter, salt))
|
||||
println("Next closer:", nc)
|
||||
// One of these NSEC4s MUST cover the next closer
|
||||
|
||||
|
||||
println("NEXT CLOSER PROOF")
|
||||
NextCloser:
|
||||
for _, nsec := range nsec4 {
|
||||
// NSEC-like, whole name
|
||||
println(nc)
|
||||
println(strings.ToUpper(HashName(nc, algo, iter, salt)))
|
||||
println(nsec.Header().Name)
|
||||
println(nsec.(*RR_NSEC4).NextDomain)
|
||||
|
||||
if CoversName(HashName(nc, algo, iter, salt), nsec.Header().Name, nsec.(*RR_NSEC4).NextDomain) {
|
||||
// Wildcard bit must be off
|
||||
println("* covers *")
|
||||
if nsec.(*RR_NSEC4).Flags&WILDCARD == 1 {
|
||||
println("Wildcard set! Error")
|
||||
println("NOT PROVEN NXDOMAIN")
|
||||
} else {
|
||||
println("Wildcard not set")
|
||||
println("NXDOMAIN IS PROVEN, IF NSEC4S ARE VALID")
|
||||
break NextCloser
|
||||
}
|
||||
// Check if the source of synthesis is covered and thus denied
|
||||
for _, nsec := range nsec3 {
|
||||
firstlab := strings.ToUpper(SplitLabels(nsec.Header().Name)[0])
|
||||
nextdom := strings.ToUpper(nsec.NextDomain)
|
||||
hashedso := HashName(so, hash, iter, salt)
|
||||
if hashedso > firstlab && hashedso < nextdom {
|
||||
sodenied = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// If the nextcloser MATCHES the owername of one of the NSEC4s we have a NODATA response
|
||||
|
||||
if !sodenied {
|
||||
return ErrDenialSo
|
||||
}
|
||||
println("NSEC3 proof succesfully proofed")
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue