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