tsig.go raw

   1  package dns
   2  
   3  import (
   4  	"crypto/hmac"
   5  	"crypto/sha1"
   6  	"crypto/sha256"
   7  	"crypto/sha512"
   8  	"encoding/binary"
   9  	"encoding/hex"
  10  	"hash"
  11  	"strconv"
  12  	"strings"
  13  	"time"
  14  )
  15  
  16  // HMAC hashing codes. These are transmitted as domain names.
  17  const (
  18  	HmacSHA1   = "hmac-sha1."
  19  	HmacSHA224 = "hmac-sha224."
  20  	HmacSHA256 = "hmac-sha256."
  21  	HmacSHA384 = "hmac-sha384."
  22  	HmacSHA512 = "hmac-sha512."
  23  
  24  	HmacMD5 = "hmac-md5.sig-alg.reg.int." // Deprecated: HmacMD5 is no longer supported.
  25  )
  26  
  27  // TsigProvider provides the API to plug-in a custom TSIG implementation.
  28  type TsigProvider interface {
  29  	// Generate is passed the DNS message to be signed and the partial TSIG RR. It returns the signature and nil, otherwise an error.
  30  	Generate(msg []byte, t *TSIG) ([]byte, error)
  31  	// Verify is passed the DNS message to be verified and the TSIG RR. If the signature is valid it will return nil, otherwise an error.
  32  	Verify(msg []byte, t *TSIG) error
  33  }
  34  
  35  type tsigHMACProvider string
  36  
  37  func (key tsigHMACProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {
  38  	// If we barf here, the caller is to blame
  39  	rawsecret, err := fromBase64([]byte(key))
  40  	if err != nil {
  41  		return nil, err
  42  	}
  43  	var h hash.Hash
  44  	switch CanonicalName(t.Algorithm) {
  45  	case HmacSHA1:
  46  		h = hmac.New(sha1.New, rawsecret)
  47  	case HmacSHA224:
  48  		h = hmac.New(sha256.New224, rawsecret)
  49  	case HmacSHA256:
  50  		h = hmac.New(sha256.New, rawsecret)
  51  	case HmacSHA384:
  52  		h = hmac.New(sha512.New384, rawsecret)
  53  	case HmacSHA512:
  54  		h = hmac.New(sha512.New, rawsecret)
  55  	default:
  56  		return nil, ErrKeyAlg
  57  	}
  58  	h.Write(msg)
  59  	return h.Sum(nil), nil
  60  }
  61  
  62  func (key tsigHMACProvider) Verify(msg []byte, t *TSIG) error {
  63  	b, err := key.Generate(msg, t)
  64  	if err != nil {
  65  		return err
  66  	}
  67  	mac, err := hex.DecodeString(t.MAC)
  68  	if err != nil {
  69  		return err
  70  	}
  71  	if !hmac.Equal(b, mac) {
  72  		return ErrSig
  73  	}
  74  	return nil
  75  }
  76  
  77  type tsigSecretProvider map[string]string
  78  
  79  func (ts tsigSecretProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {
  80  	key, ok := ts[t.Hdr.Name]
  81  	if !ok {
  82  		return nil, ErrSecret
  83  	}
  84  	return tsigHMACProvider(key).Generate(msg, t)
  85  }
  86  
  87  func (ts tsigSecretProvider) Verify(msg []byte, t *TSIG) error {
  88  	key, ok := ts[t.Hdr.Name]
  89  	if !ok {
  90  		return ErrSecret
  91  	}
  92  	return tsigHMACProvider(key).Verify(msg, t)
  93  }
  94  
  95  // TSIG is the RR the holds the transaction signature of a message.
  96  // See RFC 2845 and RFC 4635.
  97  type TSIG struct {
  98  	Hdr        RR_Header
  99  	Algorithm  string `dns:"domain-name"`
 100  	TimeSigned uint64 `dns:"uint48"`
 101  	Fudge      uint16
 102  	MACSize    uint16
 103  	MAC        string `dns:"size-hex:MACSize"`
 104  	OrigId     uint16
 105  	Error      uint16
 106  	OtherLen   uint16
 107  	OtherData  string `dns:"size-hex:OtherLen"`
 108  }
 109  
 110  // TSIG has no official presentation format, but this will suffice.
 111  
 112  func (rr *TSIG) String() string {
 113  	s := "\n;; TSIG PSEUDOSECTION:\n; " // add another semi-colon to signify TSIG does not have a presentation format
 114  	s += rr.Hdr.String() +
 115  		" " + rr.Algorithm +
 116  		" " + tsigTimeToString(rr.TimeSigned) +
 117  		" " + strconv.Itoa(int(rr.Fudge)) +
 118  		" " + strconv.Itoa(int(rr.MACSize)) +
 119  		" " + strings.ToUpper(rr.MAC) +
 120  		" " + strconv.Itoa(int(rr.OrigId)) +
 121  		" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
 122  		" " + strconv.Itoa(int(rr.OtherLen)) +
 123  		" " + rr.OtherData
 124  	return s
 125  }
 126  
 127  func (*TSIG) parse(c *zlexer, origin string) *ParseError {
 128  	return &ParseError{err: "TSIG records do not have a presentation format"}
 129  }
 130  
 131  // The following values must be put in wireformat, so that the MAC can be calculated.
 132  // RFC 2845, section 3.4.2. TSIG Variables.
 133  type tsigWireFmt struct {
 134  	// From RR_Header
 135  	Name  string `dns:"domain-name"`
 136  	Class uint16
 137  	Ttl   uint32
 138  	// Rdata of the TSIG
 139  	Algorithm  string `dns:"domain-name"`
 140  	TimeSigned uint64 `dns:"uint48"`
 141  	Fudge      uint16
 142  	// MACSize, MAC and OrigId excluded
 143  	Error     uint16
 144  	OtherLen  uint16
 145  	OtherData string `dns:"size-hex:OtherLen"`
 146  }
 147  
 148  // If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
 149  type macWireFmt struct {
 150  	MACSize uint16
 151  	MAC     string `dns:"size-hex:MACSize"`
 152  }
 153  
 154  // 3.3. Time values used in TSIG calculations
 155  type timerWireFmt struct {
 156  	TimeSigned uint64 `dns:"uint48"`
 157  	Fudge      uint16
 158  }
 159  
 160  // TsigGenerate fills out the TSIG record attached to the message.
 161  // The message should contain a "stub" TSIG RR with the algorithm, key name
 162  // (owner name of the RR), time fudge (defaults to 300 seconds) and the current
 163  // time The TSIG MAC is saved in that Tsig RR. When TsigGenerate is called for
 164  // the first time requestMAC should be set to the empty string and timersOnly to
 165  // false.
 166  func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
 167  	return TsigGenerateWithProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)
 168  }
 169  
 170  // TsigGenerateWithProvider is similar to TsigGenerate, but allows for a custom TsigProvider.
 171  func TsigGenerateWithProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {
 172  	if m.IsTsig() == nil {
 173  		panic("dns: TSIG not last RR in additional")
 174  	}
 175  
 176  	rr := m.Extra[len(m.Extra)-1].(*TSIG)
 177  	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
 178  	mbuf, err := m.Pack()
 179  	if err != nil {
 180  		return nil, "", err
 181  	}
 182  
 183  	buf, err := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
 184  	if err != nil {
 185  		return nil, "", err
 186  	}
 187  
 188  	t := new(TSIG)
 189  	// Copy all TSIG fields except MAC, its size, and time signed which are filled when signing.
 190  	*t = *rr
 191  	t.TimeSigned = 0
 192  	t.MAC = ""
 193  	t.MACSize = 0
 194  
 195  	// Sign unless there is a key or MAC validation error (RFC 8945 5.3.2)
 196  	if rr.Error != RcodeBadKey && rr.Error != RcodeBadSig {
 197  		mac, err := provider.Generate(buf, rr)
 198  		if err != nil {
 199  			return nil, "", err
 200  		}
 201  		t.TimeSigned = rr.TimeSigned
 202  		t.MAC = hex.EncodeToString(mac)
 203  		t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
 204  	}
 205  
 206  	tbuf := make([]byte, Len(t))
 207  	off, err := PackRR(t, tbuf, 0, nil, false)
 208  	if err != nil {
 209  		return nil, "", err
 210  	}
 211  	mbuf = append(mbuf, tbuf[:off]...)
 212  	// Update the ArCount directly in the buffer.
 213  	binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
 214  
 215  	return mbuf, t.MAC, nil
 216  }
 217  
 218  // TsigVerify verifies the TSIG on a message. If the signature does not
 219  // validate the returned error contains the cause. If the signature is OK, the
 220  // error is nil.
 221  func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
 222  	return tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix()))
 223  }
 224  
 225  // TsigVerifyWithProvider is similar to TsigVerify, but allows for a custom TsigProvider.
 226  func TsigVerifyWithProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {
 227  	return tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix()))
 228  }
 229  
 230  // actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests.
 231  func tsigVerify(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool, now uint64) error {
 232  	// Strip the TSIG from the incoming msg
 233  	stripped, tsig, err := stripTsig(msg)
 234  	if err != nil {
 235  		return err
 236  	}
 237  
 238  	buf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
 239  	if err != nil {
 240  		return err
 241  	}
 242  
 243  	if err := provider.Verify(buf, tsig); err != nil {
 244  		return err
 245  	}
 246  
 247  	// Fudge factor works both ways. A message can arrive before it was signed because
 248  	// of clock skew.
 249  	// We check this after verifying the signature, following draft-ietf-dnsop-rfc2845bis
 250  	// instead of RFC2845, in order to prevent a security vulnerability as reported in CVE-2017-3142/3143.
 251  	ti := now - tsig.TimeSigned
 252  	if now < tsig.TimeSigned {
 253  		ti = tsig.TimeSigned - now
 254  	}
 255  	if uint64(tsig.Fudge) < ti {
 256  		return ErrTime
 257  	}
 258  
 259  	return nil
 260  }
 261  
 262  // Create a wiredata buffer for the MAC calculation.
 263  func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) ([]byte, error) {
 264  	var buf []byte
 265  	if rr.TimeSigned == 0 {
 266  		rr.TimeSigned = uint64(time.Now().Unix())
 267  	}
 268  	if rr.Fudge == 0 {
 269  		rr.Fudge = 300 // Standard (RFC) default.
 270  	}
 271  
 272  	// Replace message ID in header with original ID from TSIG
 273  	binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)
 274  
 275  	if requestMAC != "" {
 276  		m := new(macWireFmt)
 277  		m.MACSize = uint16(len(requestMAC) / 2)
 278  		m.MAC = requestMAC
 279  		buf = make([]byte, len(requestMAC)) // long enough
 280  		n, err := packMacWire(m, buf)
 281  		if err != nil {
 282  			return nil, err
 283  		}
 284  		buf = buf[:n]
 285  	}
 286  
 287  	tsigvar := make([]byte, DefaultMsgSize)
 288  	if timersOnly {
 289  		tsig := new(timerWireFmt)
 290  		tsig.TimeSigned = rr.TimeSigned
 291  		tsig.Fudge = rr.Fudge
 292  		n, err := packTimerWire(tsig, tsigvar)
 293  		if err != nil {
 294  			return nil, err
 295  		}
 296  		tsigvar = tsigvar[:n]
 297  	} else {
 298  		tsig := new(tsigWireFmt)
 299  		tsig.Name = CanonicalName(rr.Hdr.Name)
 300  		tsig.Class = ClassANY
 301  		tsig.Ttl = rr.Hdr.Ttl
 302  		tsig.Algorithm = CanonicalName(rr.Algorithm)
 303  		tsig.TimeSigned = rr.TimeSigned
 304  		tsig.Fudge = rr.Fudge
 305  		tsig.Error = rr.Error
 306  		tsig.OtherLen = rr.OtherLen
 307  		tsig.OtherData = rr.OtherData
 308  		n, err := packTsigWire(tsig, tsigvar)
 309  		if err != nil {
 310  			return nil, err
 311  		}
 312  		tsigvar = tsigvar[:n]
 313  	}
 314  
 315  	if requestMAC != "" {
 316  		x := append(buf, msgbuf...)
 317  		buf = append(x, tsigvar...)
 318  	} else {
 319  		buf = append(msgbuf, tsigvar...)
 320  	}
 321  	return buf, nil
 322  }
 323  
 324  // Strip the TSIG from the raw message.
 325  func stripTsig(msg []byte) ([]byte, *TSIG, error) {
 326  	// Copied from msg.go's Unpack() Header, but modified.
 327  	var (
 328  		dh  Header
 329  		err error
 330  	)
 331  	off, tsigoff := 0, 0
 332  
 333  	if dh, off, err = unpackMsgHdr(msg, off); err != nil {
 334  		return nil, nil, err
 335  	}
 336  	if dh.Arcount == 0 {
 337  		return nil, nil, ErrNoSig
 338  	}
 339  
 340  	// Rcode, see msg.go Unpack()
 341  	if int(dh.Bits&0xF) == RcodeNotAuth {
 342  		return nil, nil, ErrAuth
 343  	}
 344  
 345  	for i := 0; i < int(dh.Qdcount); i++ {
 346  		_, off, err = unpackQuestion(msg, off)
 347  		if err != nil {
 348  			return nil, nil, err
 349  		}
 350  	}
 351  
 352  	_, off, err = unpackRRslice(int(dh.Ancount), msg, off)
 353  	if err != nil {
 354  		return nil, nil, err
 355  	}
 356  	_, off, err = unpackRRslice(int(dh.Nscount), msg, off)
 357  	if err != nil {
 358  		return nil, nil, err
 359  	}
 360  
 361  	rr := new(TSIG)
 362  	var extra RR
 363  	for i := 0; i < int(dh.Arcount); i++ {
 364  		tsigoff = off
 365  		extra, off, err = UnpackRR(msg, off)
 366  		if err != nil {
 367  			return nil, nil, err
 368  		}
 369  		if extra.Header().Rrtype == TypeTSIG {
 370  			rr = extra.(*TSIG)
 371  			// Adjust Arcount.
 372  			arcount := binary.BigEndian.Uint16(msg[10:])
 373  			binary.BigEndian.PutUint16(msg[10:], arcount-1)
 374  			break
 375  		}
 376  	}
 377  	if rr == nil {
 378  		return nil, nil, ErrNoSig
 379  	}
 380  	return msg[:tsigoff], rr, nil
 381  }
 382  
 383  // Translate the TSIG time signed into a date. There is no
 384  // need for RFC1982 calculations as this date is 48 bits.
 385  func tsigTimeToString(t uint64) string {
 386  	ti := time.Unix(int64(t), 0).UTC()
 387  	return ti.Format("20060102150405")
 388  }
 389  
 390  func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
 391  	// copied from zmsg.go TSIG packing
 392  	// RR_Header
 393  	off, err := PackDomainName(tw.Name, msg, 0, nil, false)
 394  	if err != nil {
 395  		return off, err
 396  	}
 397  	off, err = packUint16(tw.Class, msg, off)
 398  	if err != nil {
 399  		return off, err
 400  	}
 401  	off, err = packUint32(tw.Ttl, msg, off)
 402  	if err != nil {
 403  		return off, err
 404  	}
 405  
 406  	off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
 407  	if err != nil {
 408  		return off, err
 409  	}
 410  	off, err = packUint48(tw.TimeSigned, msg, off)
 411  	if err != nil {
 412  		return off, err
 413  	}
 414  	off, err = packUint16(tw.Fudge, msg, off)
 415  	if err != nil {
 416  		return off, err
 417  	}
 418  
 419  	off, err = packUint16(tw.Error, msg, off)
 420  	if err != nil {
 421  		return off, err
 422  	}
 423  	off, err = packUint16(tw.OtherLen, msg, off)
 424  	if err != nil {
 425  		return off, err
 426  	}
 427  	off, err = packStringHex(tw.OtherData, msg, off)
 428  	if err != nil {
 429  		return off, err
 430  	}
 431  	return off, nil
 432  }
 433  
 434  func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
 435  	off, err := packUint16(mw.MACSize, msg, 0)
 436  	if err != nil {
 437  		return off, err
 438  	}
 439  	off, err = packStringHex(mw.MAC, msg, off)
 440  	if err != nil {
 441  		return off, err
 442  	}
 443  	return off, nil
 444  }
 445  
 446  func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
 447  	off, err := packUint48(tw.TimeSigned, msg, 0)
 448  	if err != nil {
 449  		return off, err
 450  	}
 451  	off, err = packUint16(tw.Fudge, msg, off)
 452  	if err != nil {
 453  		return off, err
 454  	}
 455  	return off, nil
 456  }
 457