From ea789b6f239637463dc47bdafa2b60d65ff5f2c8 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 19 Jan 2012 19:48:09 +0100 Subject: [PATCH] Add nsec3 NXDOMAIN proof --- _examples/q/q.go | 4 +- msg.go | 57 +++++++-------- nsec3.go | 176 +++++++++++++++++------------------------------ 3 files changed, 94 insertions(+), 143 deletions(-) diff --git a/_examples/q/q.go b/_examples/q/q.go index 065fe643..d3484d62 100644 --- a/_examples/q/q.go +++ b/_examples/q/q.go @@ -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) diff --git a/msg.go b/msg.go index 20bbcc71..39c5e6ab 100644 --- a/msg.go +++ b/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)) } diff --git a/nsec3.go b/nsec3.go index d6f91259..8bc5e695 100644 --- a/nsec3.go +++ b/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 }