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