From 9bc0f3ed6b6338d30bf1708aab385338b8c646a8 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Mon, 21 Mar 2011 10:51:28 +0100 Subject: [PATCH] More TSIG stuff --- dns.go | 20 +++++-- resolver.go | 27 +-------- tsig.go | 166 ++-------------------------------------------------- types.go | 32 ++++++++++ 4 files changed, 56 insertions(+), 189 deletions(-) diff --git a/dns.go b/dns.go index 79c04d70..4d45bc7f 100644 --- a/dns.go +++ b/dns.go @@ -110,7 +110,7 @@ func (d *Conn) Read(p []byte) (n int, err os.Error) { } if d.Tsig != nil { // Check the TSIG that we should be read - d.Tsig.Verify(p) + _, err = d.Tsig.Verify(p) } return } @@ -121,6 +121,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { } var attempts int + var q []byte if d.Attempts == 0 { attempts = 1 } else { @@ -128,10 +129,21 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { } d.SetTimeout() + if d.Tsig != nil { + // Create a new buffer with the TSIG added. + var ok bool + q, ok = d.Tsig.Generate(p) + if !ok { + // dikke shit + } + } else { + q = p + } + switch { case d.UDP != nil: for a := 0; a < attempts; a++ { - n, err = d.UDP.WriteTo(p, d.Addr) + n, err = d.UDP.WriteTo(q, d.Addr) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue @@ -142,7 +154,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { case d.TCP != nil: for a := 0; a < attempts; a++ { l := make([]byte, 2) - l[0], l[1] = packUint16(uint16(len(p))) + l[0], l[1] = packUint16(uint16(len(q))) n, err = d.TCP.Write(l) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { @@ -153,7 +165,7 @@ func (d *Conn) Write(p []byte) (n int, err os.Error) { if n != 2 { return n, &Error{Error: "Write failure"} } - n, err = d.TCP.Write(p) + n, err = d.TCP.Write(q) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { continue diff --git a/resolver.go b/resolver.go index 3d1c6b26..4b44bda2 100644 --- a/resolver.go +++ b/resolver.go @@ -102,7 +102,6 @@ type Xfr struct { // have Xfr.Add set to true otherwise it is false. // Channel m is closed when the IXFR ends. func (res *Resolver) Ixfr(q *Msg, m chan Xfr) { - // TSIG var ( x Xfr inb []byte @@ -205,7 +204,7 @@ Server: // returned over the channel, so the caller will receive // the zone as-is. Xfr.Add is always true. // The channel is closed to signal the end of the AXFR. -func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { +func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, t *Tsig) { var inb []byte in := new(Msg) port, err := check(res, q) @@ -219,16 +218,6 @@ func (res *Resolver) AxfrTSIG(q *Msg, m chan Xfr, secret string) { return } - var tsig bool - var reqmac string - // Check if there is a TSIG added to the request msg - if len(q.Extra) > 0 { - tsig = q.Extra[len(q.Extra)-1].Header().Rrtype == TypeTSIG - if tsig { - reqmac = q.Extra[len(q.Extra)-1].(*RR_TSIG).MAC - } - } - Server: for i := 0; i < len(res.Servers); i++ { server := res.Servers[i] + ":" + port @@ -239,6 +228,7 @@ Server: d := new(Conn) d.TCP = c.(*net.TCPConn) d.Addr = d.TCP.RemoteAddr() + d.Tsig = t first := true defer c.Close() // TODO(mg): if not open? @@ -259,19 +249,6 @@ Server: return } - if tsig && len(in.Extra) > 0 { // What if not included? - t := in.Extra[len(in.Extra)-1] - if t.Header().Rrtype == TypeTSIG { - if t.(*RR_TSIG).Verify(inb, secret, reqmac, first) { - // Set the MAC for the next round. - reqmac = t.(*RR_TSIG).MAC - } else { - c.Close() - return - } - } - } - if first { if !checkXfrSOA(in, true) { c.Close() diff --git a/tsig.go b/tsig.go index 375af43c..403c0c13 100644 --- a/tsig.go +++ b/tsig.go @@ -4,8 +4,8 @@ package dns // RFC 2845 and RFC 4635 import ( "io" + "os" "time" - "strconv" "strings" "crypto/hmac" "encoding/hex" @@ -38,45 +38,6 @@ const ( HmacSHA256 = "hmac-sha256." ) -type RR_TSIG struct { - Hdr RR_Header - Algorithm string "domain-name" - TimeSigned uint64 - Fudge uint16 - MACSize uint16 - MAC string "size-hex" - OrigId uint16 - Error uint16 - OtherLen uint16 - OtherData string "size-hex" -} - -func (rr *RR_TSIG) Header() *RR_Header { - return &rr.Hdr -} - -func (rr *RR_TSIG) SetDefaults() { - rr.Header().Ttl = 0 - rr.Header().Class = ClassANY - rr.Header().Rrtype = TypeTSIG - rr.Fudge = 300 - rr.Algorithm = HmacMD5 -} - -// TSIG has no official presentation format, but this will suffice. -func (rr *RR_TSIG) String() string { - return rr.Hdr.String() + - " " + rr.Algorithm + - " " + tsigTimeToDate(rr.TimeSigned) + - " " + strconv.Itoa(int(rr.Fudge)) + - " " + strconv.Itoa(int(rr.MACSize)) + - " " + strings.ToUpper(rr.MAC) + - " " + strconv.Itoa(int(rr.OrigId)) + - " " + strconv.Itoa(int(rr.Error)) + - " " + strconv.Itoa(int(rr.OtherLen)) + - " " + rr.OtherData -} - // The following values must be put in wireformat, so that the MAC can be calculated. // RFC 2845, section 3.4.2. TSIG Variables. type tsigWireFmt struct { @@ -149,87 +110,27 @@ func (t *Tsig) Generate(msg []byte) ([]byte, bool) { return send, ok } -// Generate the HMAC for message. The TSIG RR is modified -// to include the MAC and MACSize. Note the the msg Id must -// already be set, otherwise the MAC will not be correct when -// the message is send. -// The string 'secret' must be encoded in base64. -func (t *RR_TSIG) Generate(m *Msg, secret string) bool { - rawsecret, err := packBase64([]byte(secret)) - if err != nil { - return false - } - t.OrigId = m.MsgHdr.Id - - msg, ok := m.Pack() - if !ok { - return false - } - buf, ok1 := tsigToBuf(t, msg, "", true) - if !ok1 { - return false - } - h := hmac.NewMD5([]byte(rawsecret)) - io.WriteString(h, string(buf)) - - t.MAC = hex.EncodeToString(h.Sum()) - t.MACSize = uint16(len(h.Sum())) // Needs to be "on-the-wire" size. - if !ok { - return false - } - return true -} - // Verify a TSIG on a message. All relevant data should // be set in the Tsig structure. -func (t *Tsig) Verify(msg []byte) bool { +func (t *Tsig) Verify(msg []byte) (bool, os.Error) { rawsecret, err := packBase64([]byte(t.Secret)) if err != nil { - return false + return false, err } // Stipped the TSIG from the incoming msg stripped, ok := stripTsig(msg) if !ok { - return false + return false, &Error{Error: "Failed to strip tsig"} } buf, ok := t.Buffer(stripped) if !ok { - return false + return false, &Error{Error: "Failed to convert to raw buffer"} } h := hmac.NewMD5([]byte(rawsecret)) io.WriteString(h, string(buf)) - return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) -} - -// Verify a TSIG. The message should be the complete with -// the TSIG record still attached (as the last rr in the Additional -// section). Return true on success. -// The secret is a base64 encoded string with the secret. -func (t *RR_TSIG) Verify(msg []byte, secret, reqmac string, timers bool) bool { - rawsecret, err := packBase64([]byte(secret)) - if err != nil { - return false - } - - if t.Header().Rrtype != TypeTSIG { - return false - } - - // t.OrigId -- need to check - stripped, ok := stripTsig(msg) - if !ok { - return false - } - buf, ok := tsigToBuf(t, stripped, reqmac, timers) - if !ok { - return false - } - - h := hmac.NewMD5([]byte(rawsecret)) - io.WriteString(h, string(buf)) - return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC) + return strings.ToUpper(hex.EncodeToString(h.Sum())) == strings.ToUpper(t.MAC), nil } // Create a wiredata buffer for the MAC calculation @@ -287,61 +188,6 @@ func (t *Tsig) Buffer(msg []byte) ([]byte, bool) { return buf, true } -// Create the buffer which we use for the MAC calculation. -func tsigToBuf(rr *RR_TSIG, msg []byte, reqmac string, timers bool) ([]byte, bool) { - var ( - macbuf []byte - buf []byte - ) - - if reqmac != "" { - m := new(macWireFmt) - m.MACSize = uint16(len(reqmac) / 2) - m.MAC = reqmac - macbuf = make([]byte, len(reqmac)) // reqmac should be twice as long - n, ok := packStruct(m, macbuf, 0) - if !ok { - return nil, false - } - macbuf = macbuf[:n] - } - - tsigvar := make([]byte, DefaultMsgSize) - if timers { - tsig := new(tsigWireFmt) - tsig.Name = strings.ToLower(rr.Header().Name) - tsig.Class = rr.Header().Class - tsig.Ttl = rr.Header().Ttl - tsig.Algorithm = strings.ToLower(rr.Algorithm) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - tsig.Error = rr.Error - tsig.OtherLen = rr.OtherLen - tsig.OtherData = rr.OtherData - n, ok1 := packStruct(tsig, tsigvar, 0) - if !ok1 { - return nil, false - } - tsigvar = tsigvar[:n] - } else { - tsig := new(timerWireFmt) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - n, ok1 := packStruct(tsig, tsigvar, 0) - if !ok1 { - return nil, false - } - tsigvar = tsigvar[:n] - } - if reqmac != "" { - x := append(macbuf, msg...) - buf = append(x, tsigvar...) - } else { - buf = append(msg, tsigvar...) - } - return buf, true -} - // Strip the TSIG from the pkt. func stripTsig(orig []byte) ([]byte, bool) { // Copied from msg.go's Unpack() diff --git a/types.go b/types.go index 774bf4a7..cdf3b620 100644 --- a/types.go +++ b/types.go @@ -739,6 +739,38 @@ func (rr *RR_DHCID) String() string { return rr.Hdr.String() + rr.Digest } +// RFC 2845. +type RR_TSIG struct { + Hdr RR_Header + Algorithm string "domain-name" + TimeSigned uint64 + Fudge uint16 + MACSize uint16 + MAC string "size-hex" + OrigId uint16 + Error uint16 + OtherLen uint16 + OtherData string "size-hex" +} + +func (rr *RR_TSIG) Header() *RR_Header { + return &rr.Hdr +} + +// TSIG has no official presentation format, but this will suffice. +func (rr *RR_TSIG) String() string { + return rr.Hdr.String() + + " " + rr.Algorithm + + " " + tsigTimeToDate(rr.TimeSigned) + + " " + strconv.Itoa(int(rr.Fudge)) + + " " + strconv.Itoa(int(rr.MACSize)) + + " " + strings.ToUpper(rr.MAC) + + " " + strconv.Itoa(int(rr.OrigId)) + + " " + strconv.Itoa(int(rr.Error)) + + " " + strconv.Itoa(int(rr.OtherLen)) + + " " + rr.OtherData +} + // Translate the RRSIG's incep. and expir. time to the correct date. // Taking into account serial arithmetic (RFC 1982) func timeToDate(t uint32) string {