Fix client side TSIG
Redesign of TSIG. Validation is on the TOOD - this can be done in the same way as in the server.
This commit is contained in:
parent
172c89675c
commit
3232814d1b
|
@ -6,15 +6,13 @@ need to be fixed.
|
|||
* Speed, we can always go faster. A simple reflect server now hits 35/45K qps
|
||||
* go test; only works correct on my machine
|
||||
* Add handy zone data structure (r/b tree)? Or not...
|
||||
* Use the Exchange structure to deal with errors when resolving, esp. Timeout
|
||||
* Add tsig check in 'q'?
|
||||
* Tsig is handled in the library, api for querying tsig status
|
||||
* Query source address?
|
||||
|
||||
* NSECx bitmap length
|
||||
array of 256 block lens set to 0. scan RRs, save highest RR / 8 in
|
||||
each block. len is 2 * # non-0 blocks + sum block len
|
||||
|
||||
We now allocate 32 bytes for each nsec3 seen
|
||||
|
||||
## Examples to add
|
||||
|
||||
* Nameserver, with a small zone, 1 KSK and online signing;
|
||||
|
|
26
client.go
26
client.go
|
@ -39,7 +39,7 @@ type reply struct {
|
|||
conn net.Conn
|
||||
tsigRequestMAC string
|
||||
tsigTimersOnly bool
|
||||
tsigStatus int
|
||||
tsigStatus int
|
||||
}
|
||||
|
||||
// A Request is a incoming message from a Client.
|
||||
|
@ -150,6 +150,7 @@ func NewClient() *Client {
|
|||
c.QueryChan = DefaultQueryChan
|
||||
c.ReadTimeout = 2 * 1e9
|
||||
c.WriteTimeout = 2 * 1e9
|
||||
c.TsigSecret = make(map[string]string)
|
||||
return c
|
||||
}
|
||||
|
||||
|
@ -382,25 +383,18 @@ func (w *reply) readClient(p []byte) (n int, err error) {
|
|||
// signature is calculated.
|
||||
func (w *reply) Send(m *Msg) error {
|
||||
if m.IsTsig() {
|
||||
secret := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
|
||||
_, ok := w.Client().TsigSecret[secret]
|
||||
if !ok {
|
||||
name := m.Extra[len(m.Extra)-1].(*RR_TSIG).Hdr.Name
|
||||
if _, ok := w.Client().TsigSecret[name]; !ok {
|
||||
return ErrSecret
|
||||
}
|
||||
// TODO(mg): compression makes this fail
|
||||
if err := TsigGenerate(m, w.Client().TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly); err != nil {
|
||||
out, mac, err := TsigGenerate(m, w.Client().TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.tsigRequestMAC = mac
|
||||
if _, err = w.writeClient(out); err != nil {
|
||||
return err
|
||||
}
|
||||
w.tsigRequestMAC = m.Extra[len(m.Extra)-1].(*RR_TSIG).MAC // Save the requestMAC for the next packet
|
||||
}
|
||||
out, ok := m.Pack()
|
||||
if !ok {
|
||||
return ErrPack
|
||||
}
|
||||
// Tsig calculation should happen here
|
||||
_, err := w.writeClient(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
35
ex/q/q.go
35
ex/q/q.go
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dnskey *dns.RR_DNSKEY
|
||||
|
@ -32,7 +33,7 @@ func main() {
|
|||
short := flag.Bool("short", false, "abbreviate long DNSSEC records")
|
||||
check := flag.Bool("check", false, "check internal DNSSEC consistency")
|
||||
anchor := flag.String("anchor", "", "use the DNSKEY in this file for interal DNSSEC consistency")
|
||||
//tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key")
|
||||
tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key")
|
||||
port := flag.Int("port", 53, "port number to use")
|
||||
aa := flag.Bool("aa", false, "set AA flag in query")
|
||||
ad := flag.Bool("ad", false, "set AD flag in query")
|
||||
|
@ -127,9 +128,7 @@ Flags:
|
|||
nameserver = string([]byte(nameserver)[1:]) // chop off @
|
||||
nameserver += ":" + strconv.Itoa(*port)
|
||||
|
||||
// ipv6 todo
|
||||
// We use the async query handling, just to show how
|
||||
// it is to be used.
|
||||
// We use the async query handling, just to show how it is to be used.
|
||||
dns.HandleQueryFunc(".", q)
|
||||
dns.ListenAndQuery(nil, nil)
|
||||
c := dns.NewClient()
|
||||
|
@ -163,6 +162,16 @@ Flags:
|
|||
if *query {
|
||||
fmt.Printf("%s\n", m.String())
|
||||
}
|
||||
// Add tsig
|
||||
if *tsig != "" {
|
||||
if algo, name, secret, ok := tsigKeyParse(*tsig); ok {
|
||||
m.SetTsig(name, algo, 300, uint64(time.Now().Unix()))
|
||||
c.TsigSecret[name] = secret;
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "TSIG key error\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Do(m, nameserver)
|
||||
}
|
||||
|
||||
|
@ -219,6 +228,24 @@ forever:
|
|||
}
|
||||
}
|
||||
|
||||
func tsigKeyParse(s string) (algo, name, secret string, ok bool) {
|
||||
s1 := strings.SplitN(s, ":", 3)
|
||||
switch len(s1) {
|
||||
case 2:
|
||||
return "hmac-md5.sig-alg.reg.int.", s1[0], s1[1], true
|
||||
case 3:
|
||||
switch s1[0] {
|
||||
case "hmac-md5":
|
||||
return "hmac-md5.sig-alg.reg.int.", s1[0], s1[1], true
|
||||
case "hmac-sha1":
|
||||
return "hmac-sha1.", s1[1], s1[2], true
|
||||
case "hmac-sha256":
|
||||
return "hmac-sha256.", s1[1], s1[2], true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sectionCheck(set []dns.RR, server string, tcp bool) {
|
||||
var key *dns.RR_DNSKEY
|
||||
for _, rr := range set {
|
||||
|
|
2
msg.go
2
msg.go
|
@ -42,7 +42,7 @@ var (
|
|||
ErrTime error = &Error{Err: "dns: bad time"}
|
||||
ErrNoSig error = &Error{Err: "dns: no signature found"}
|
||||
ErrSig error = &Error{Err: "dns: bad signature"}
|
||||
ErrSecret error = &Error{Err: "dns: no secret defined"}
|
||||
ErrSecret error = &Error{Err: "dns: no secrets defined"}
|
||||
ErrSigGen error = &Error{Err: "dns: bad signature generation"}
|
||||
ErrAuth error = &Error{Err: "dns: bad authentication"}
|
||||
ErrXfrSoa error = &Error{Err: "dns: no SOA seen"}
|
||||
|
|
|
@ -189,10 +189,10 @@ func TestParseDirectiveMisc(t *testing.T) {
|
|||
// Another one hear, geared to NSECx
|
||||
func TestParseNSEC(t *testing.T) {
|
||||
nsectests := map[string]string{
|
||||
"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F",
|
||||
"nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F",
|
||||
"p2209hipbpnm681knjnu0m1febshlv4e.nl. IN NSEC3 1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM": "p2209hipbpnm681knjnu0m1febshlv4e.nl.\t3600\tIN\tNSEC3\t1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM",
|
||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC",
|
||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC TYPE65534",
|
||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC",
|
||||
"localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC TYPE65534",
|
||||
}
|
||||
for i, o := range nsectests {
|
||||
rr, e := NewRR(i)
|
||||
|
|
40
rawmsg.go
40
rawmsg.go
|
@ -4,24 +4,39 @@
|
|||
|
||||
package dns
|
||||
|
||||
// RawSetId sets the message ID in buf. The offset 'off' must
|
||||
// be positioned at the beginning of the message.
|
||||
func RawSetId(msg []byte, off int, id uint16) bool {
|
||||
msg[off], msg[off+1] = packUint16(id)
|
||||
return true
|
||||
// RawSetId sets the message ID in buf.
|
||||
func RawSetId(msg []byte, i uint16) {
|
||||
msg[0], msg[1] = packUint16(i)
|
||||
}
|
||||
|
||||
// RawSetQuestionLen sets the len of the question section.
|
||||
func RawSetQuestionLen(msg []byte, i uint16) {
|
||||
msg[4], msg[5] = packUint16(i)
|
||||
}
|
||||
|
||||
// RawSetAnswerLen sets the len of the question section.
|
||||
func RawSetAnswerLen(msg []byte, i uint16) {
|
||||
msg[6], msg[7] = packUint16(i)
|
||||
}
|
||||
|
||||
// RawSetsNsLen sets the len of the question section.
|
||||
func RawSetNsLen(msg []byte, i uint16) {
|
||||
msg[8], msg[9] = packUint16(i)
|
||||
}
|
||||
|
||||
// RawSetExtraLen sets the len of the question section.
|
||||
func RawSetExtraLen(msg []byte, i uint16) {
|
||||
msg[10], msg[11] = packUint16(i)
|
||||
}
|
||||
|
||||
// RawSetRdlength sets the rdlength in the header of
|
||||
// the RR. The offset 'off' must be positioned at the
|
||||
// start of the header of the RR, 'end' must be the
|
||||
// end of the RR.
|
||||
func RawSetRdlength(msg []byte, off, end int) bool {
|
||||
// end of the RR. There is no check if we overrun the buffer.
|
||||
func RawSetRdlength(msg []byte, off, end int) {
|
||||
// We are at the start of the header, walk the domainname (might be compressed)
|
||||
Loop:
|
||||
for {
|
||||
if off > len(msg) {
|
||||
return false
|
||||
}
|
||||
c := int(msg[off])
|
||||
off++
|
||||
switch c & 0xC0 {
|
||||
|
@ -40,11 +55,8 @@ Loop:
|
|||
// The domainname has been seen, we at the start of the fixed part in the header.
|
||||
// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
|
||||
off += 2 + 2 + 4
|
||||
if off+1 > len(msg) {
|
||||
return false
|
||||
}
|
||||
//off+1 is the end of the header, 'end' is the end of the rr
|
||||
//so 'end' - 'off+2' is the lenght of the rdata
|
||||
msg[off], msg[off+1] = packUint16(uint16(end - (off + 2)))
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
|
48
tsig.go
48
tsig.go
|
@ -82,34 +82,33 @@ type timerWireFmt struct {
|
|||
Fudge uint16
|
||||
}
|
||||
|
||||
// TsigGenerate adds an TSIG RR to a message. The message should contain
|
||||
// a "stub" TsigRR with the algorithm, key name (owner name of the RR),
|
||||
// time fudge (defaults to 300 seconds) and the current time
|
||||
// The TSIG MAC is saved in that Tsig RR.
|
||||
// When TsigGenerate is called for the
|
||||
// first time requestMAC is set to the empty string.
|
||||
// If something goes wrong an error is returned, otherwise it is nil.
|
||||
// TODO this needs to work on []byte, not *Msg, to take
|
||||
// compression into account
|
||||
// This
|
||||
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) error {
|
||||
// TsigGenerate fills out the TSIG record attached to the message.
|
||||
// The message should contain
|
||||
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
|
||||
// time fudge (defaults to 300 seconds) and the current time
|
||||
// The TSIG MAC is saved in that Tsig RR.
|
||||
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
|
||||
// timersOnly is false.
|
||||
// If something goes wrong an error is returned, otherwise it is nil.
|
||||
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
||||
if !m.IsTsig() {
|
||||
// panic? panic?
|
||||
panic("TSIG not last RR in additional")
|
||||
}
|
||||
// If we barf here, the caller is to blame
|
||||
rawsecret, err := packBase64([]byte(secret))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
rr := m.Extra[len(m.Extra)-1].(*RR_TSIG)
|
||||
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
|
||||
mbuf, _ := m.Pack()
|
||||
mbuf, ok := m.Pack()
|
||||
if !ok {
|
||||
return nil, "", ErrPack
|
||||
}
|
||||
buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
|
||||
|
||||
t := new(RR_TSIG)
|
||||
|
||||
var h hash.Hash
|
||||
switch rr.Algorithm {
|
||||
case HmacMD5:
|
||||
|
@ -119,10 +118,10 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) error {
|
|||
case HmacSHA256:
|
||||
h = hmac.New(sha256.New, []byte(rawsecret))
|
||||
default:
|
||||
return ErrKeyAlg
|
||||
return nil, "", ErrKeyAlg
|
||||
}
|
||||
|
||||
t.MAC = hex.EncodeToString(h.Sum(buf))
|
||||
io.WriteString(h, string(buf))
|
||||
t.MAC = hex.EncodeToString(h.Sum(nil))
|
||||
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
|
||||
|
||||
t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
|
||||
|
@ -130,9 +129,16 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) error {
|
|||
t.TimeSigned = rr.TimeSigned
|
||||
t.Algorithm = rr.Algorithm
|
||||
t.OrigId = m.MsgHdr.Id
|
||||
|
||||
m.Extra = append(m.Extra, t)
|
||||
return nil
|
||||
|
||||
tbuf := make([]byte, t.Len())
|
||||
if off, ok := packRR(t, tbuf, 0, nil, false); ok {
|
||||
tbuf = tbuf[:off] // reset to actual size used
|
||||
} else {
|
||||
return nil, "", ErrPack
|
||||
}
|
||||
mbuf = append(mbuf, tbuf...)
|
||||
RawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
|
||||
return mbuf, t.MAC, nil
|
||||
}
|
||||
|
||||
// TsigVerify verifies the TSIG on a message.
|
||||
|
|
4
types.go
4
types.go
|
@ -178,7 +178,7 @@ func (rr *RR_ANY) Len() int {
|
|||
}
|
||||
|
||||
type RR_CNAME struct {
|
||||
Hdr RR_Header
|
||||
Hdr RR_Header
|
||||
Target string "cdomain-name"
|
||||
}
|
||||
|
||||
|
@ -1096,7 +1096,7 @@ func dateToTime(s string) (uint32, error) {
|
|||
// need for RFC1982 calculations as this date is 48 bits
|
||||
func tsigTimeToDate(t uint64) string {
|
||||
// only use the lower 48 bits, TODO(mg), check for 48 bit size
|
||||
return ""
|
||||
return "TODO"
|
||||
/*
|
||||
ti := time.Unix(int64(t), 0).Unix()
|
||||
return ti.Format("20060102150405")
|
||||
|
|
|
@ -666,7 +666,7 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
|
|||
|
||||
rr.TypeBitMap = make([]uint16, 0)
|
||||
var (
|
||||
k uint16
|
||||
k uint16
|
||||
ok bool
|
||||
)
|
||||
l = <-c
|
||||
|
@ -676,7 +676,7 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
|
|||
// Ok
|
||||
case _STRING:
|
||||
if k, ok = Str_rr[strings.ToUpper(l.token)]; !ok {
|
||||
if k, ok = typeToInt(l.token); ! ok {
|
||||
if k, ok = typeToInt(l.token); !ok {
|
||||
return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue