gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

tsig.go (12028B)


      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 }