...

Source file src/github.com/miekg/dns/tsig.go

Documentation: github.com/miekg/dns

     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  

View as plain text