tsig.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. package dns
  2. import (
  3. "crypto/hmac"
  4. "crypto/md5"
  5. "crypto/sha1"
  6. "crypto/sha256"
  7. "crypto/sha512"
  8. "encoding/hex"
  9. "hash"
  10. "io"
  11. "strconv"
  12. "strings"
  13. "time"
  14. )
  15. // HMAC hashing codes. These are transmitted as domain names.
  16. const (
  17. HmacMD5 = "hmac-md5.sig-alg.reg.int."
  18. HmacSHA1 = "hmac-sha1."
  19. HmacSHA256 = "hmac-sha256."
  20. HmacSHA512 = "hmac-sha512."
  21. )
  22. // TSIG is the RR the holds the transaction signature of a message.
  23. // See RFC 2845 and RFC 4635.
  24. type TSIG struct {
  25. Hdr RR_Header
  26. Algorithm string `dns:"domain-name"`
  27. TimeSigned uint64 `dns:"uint48"`
  28. Fudge uint16
  29. MACSize uint16
  30. MAC string `dns:"size-hex"`
  31. OrigId uint16
  32. Error uint16
  33. OtherLen uint16
  34. OtherData string `dns:"size-hex"`
  35. }
  36. // TSIG has no official presentation format, but this will suffice.
  37. func (rr *TSIG) String() string {
  38. s := "\n;; TSIG PSEUDOSECTION:\n"
  39. s += rr.Hdr.String() +
  40. " " + rr.Algorithm +
  41. " " + tsigTimeToString(rr.TimeSigned) +
  42. " " + strconv.Itoa(int(rr.Fudge)) +
  43. " " + strconv.Itoa(int(rr.MACSize)) +
  44. " " + strings.ToUpper(rr.MAC) +
  45. " " + strconv.Itoa(int(rr.OrigId)) +
  46. " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
  47. " " + strconv.Itoa(int(rr.OtherLen)) +
  48. " " + rr.OtherData
  49. return s
  50. }
  51. // The following values must be put in wireformat, so that the MAC can be calculated.
  52. // RFC 2845, section 3.4.2. TSIG Variables.
  53. type tsigWireFmt struct {
  54. // From RR_Header
  55. Name string `dns:"domain-name"`
  56. Class uint16
  57. Ttl uint32
  58. // Rdata of the TSIG
  59. Algorithm string `dns:"domain-name"`
  60. TimeSigned uint64 `dns:"uint48"`
  61. Fudge uint16
  62. // MACSize, MAC and OrigId excluded
  63. Error uint16
  64. OtherLen uint16
  65. OtherData string `dns:"size-hex"`
  66. }
  67. // If we have the MAC use this type to convert it to wiredata.
  68. // Section 3.4.3. Request MAC
  69. type macWireFmt struct {
  70. MACSize uint16
  71. MAC string `dns:"size-hex"`
  72. }
  73. // 3.3. Time values used in TSIG calculations
  74. type timerWireFmt struct {
  75. TimeSigned uint64 `dns:"uint48"`
  76. Fudge uint16
  77. }
  78. // TsigGenerate fills out the TSIG record attached to the message.
  79. // The message should contain
  80. // a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
  81. // time fudge (defaults to 300 seconds) and the current time
  82. // The TSIG MAC is saved in that Tsig RR.
  83. // When TsigGenerate is called for the first time requestMAC is set to the empty string and
  84. // timersOnly is false.
  85. // If something goes wrong an error is returned, otherwise it is nil.
  86. func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
  87. if m.IsTsig() == nil {
  88. panic("dns: TSIG not last RR in additional")
  89. }
  90. // If we barf here, the caller is to blame
  91. rawsecret, err := fromBase64([]byte(secret))
  92. if err != nil {
  93. return nil, "", err
  94. }
  95. rr := m.Extra[len(m.Extra)-1].(*TSIG)
  96. m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
  97. mbuf, err := m.Pack()
  98. if err != nil {
  99. return nil, "", err
  100. }
  101. buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
  102. t := new(TSIG)
  103. var h hash.Hash
  104. switch rr.Algorithm {
  105. case HmacMD5:
  106. h = hmac.New(md5.New, []byte(rawsecret))
  107. case HmacSHA1:
  108. h = hmac.New(sha1.New, []byte(rawsecret))
  109. case HmacSHA256:
  110. h = hmac.New(sha256.New, []byte(rawsecret))
  111. case HmacSHA512:
  112. h = hmac.New(sha512.New, []byte(rawsecret))
  113. default:
  114. return nil, "", ErrKeyAlg
  115. }
  116. io.WriteString(h, string(buf))
  117. t.MAC = hex.EncodeToString(h.Sum(nil))
  118. t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
  119. t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
  120. t.Fudge = rr.Fudge
  121. t.TimeSigned = rr.TimeSigned
  122. t.Algorithm = rr.Algorithm
  123. t.OrigId = m.Id
  124. tbuf := make([]byte, t.len())
  125. if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
  126. tbuf = tbuf[:off] // reset to actual size used
  127. } else {
  128. return nil, "", err
  129. }
  130. mbuf = append(mbuf, tbuf...)
  131. rawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
  132. return mbuf, t.MAC, nil
  133. }
  134. // TsigVerify verifies the TSIG on a message.
  135. // If the signature does not validate err contains the
  136. // error, otherwise it is nil.
  137. func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
  138. rawsecret, err := fromBase64([]byte(secret))
  139. if err != nil {
  140. return err
  141. }
  142. // Strip the TSIG from the incoming msg
  143. stripped, tsig, err := stripTsig(msg)
  144. if err != nil {
  145. return err
  146. }
  147. msgMAC, err := hex.DecodeString(tsig.MAC)
  148. if err != nil {
  149. return err
  150. }
  151. buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
  152. // Fudge factor works both ways. A message can arrive before it was signed because
  153. // of clock skew.
  154. now := uint64(time.Now().Unix())
  155. ti := now - tsig.TimeSigned
  156. if now < tsig.TimeSigned {
  157. ti = tsig.TimeSigned - now
  158. }
  159. if uint64(tsig.Fudge) < ti {
  160. return ErrTime
  161. }
  162. var h hash.Hash
  163. switch tsig.Algorithm {
  164. case HmacMD5:
  165. h = hmac.New(md5.New, rawsecret)
  166. case HmacSHA1:
  167. h = hmac.New(sha1.New, rawsecret)
  168. case HmacSHA256:
  169. h = hmac.New(sha256.New, rawsecret)
  170. case HmacSHA512:
  171. h = hmac.New(sha512.New, rawsecret)
  172. default:
  173. return ErrKeyAlg
  174. }
  175. h.Write(buf)
  176. if !hmac.Equal(h.Sum(nil), msgMAC) {
  177. return ErrSig
  178. }
  179. return nil
  180. }
  181. // Create a wiredata buffer for the MAC calculation.
  182. func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
  183. var buf []byte
  184. if rr.TimeSigned == 0 {
  185. rr.TimeSigned = uint64(time.Now().Unix())
  186. }
  187. if rr.Fudge == 0 {
  188. rr.Fudge = 300 // Standard (RFC) default.
  189. }
  190. if requestMAC != "" {
  191. m := new(macWireFmt)
  192. m.MACSize = uint16(len(requestMAC) / 2)
  193. m.MAC = requestMAC
  194. buf = make([]byte, len(requestMAC)) // long enough
  195. n, _ := PackStruct(m, buf, 0)
  196. buf = buf[:n]
  197. }
  198. tsigvar := make([]byte, DefaultMsgSize)
  199. if timersOnly {
  200. tsig := new(timerWireFmt)
  201. tsig.TimeSigned = rr.TimeSigned
  202. tsig.Fudge = rr.Fudge
  203. n, _ := PackStruct(tsig, tsigvar, 0)
  204. tsigvar = tsigvar[:n]
  205. } else {
  206. tsig := new(tsigWireFmt)
  207. tsig.Name = strings.ToLower(rr.Hdr.Name)
  208. tsig.Class = ClassANY
  209. tsig.Ttl = rr.Hdr.Ttl
  210. tsig.Algorithm = strings.ToLower(rr.Algorithm)
  211. tsig.TimeSigned = rr.TimeSigned
  212. tsig.Fudge = rr.Fudge
  213. tsig.Error = rr.Error
  214. tsig.OtherLen = rr.OtherLen
  215. tsig.OtherData = rr.OtherData
  216. n, _ := PackStruct(tsig, tsigvar, 0)
  217. tsigvar = tsigvar[:n]
  218. }
  219. if requestMAC != "" {
  220. x := append(buf, msgbuf...)
  221. buf = append(x, tsigvar...)
  222. } else {
  223. buf = append(msgbuf, tsigvar...)
  224. }
  225. return buf
  226. }
  227. // Strip the TSIG from the raw message.
  228. func stripTsig(msg []byte) ([]byte, *TSIG, error) {
  229. // Copied from msg.go's Unpack()
  230. // Header.
  231. var dh Header
  232. var err error
  233. dns := new(Msg)
  234. rr := new(TSIG)
  235. off := 0
  236. tsigoff := 0
  237. if off, err = UnpackStruct(&dh, msg, off); err != nil {
  238. return nil, nil, err
  239. }
  240. if dh.Arcount == 0 {
  241. return nil, nil, ErrNoSig
  242. }
  243. // Rcode, see msg.go Unpack()
  244. if int(dh.Bits&0xF) == RcodeNotAuth {
  245. return nil, nil, ErrAuth
  246. }
  247. // Arrays.
  248. dns.Question = make([]Question, dh.Qdcount)
  249. dns.Answer = make([]RR, dh.Ancount)
  250. dns.Ns = make([]RR, dh.Nscount)
  251. dns.Extra = make([]RR, dh.Arcount)
  252. for i := 0; i < len(dns.Question); i++ {
  253. off, err = UnpackStruct(&dns.Question[i], msg, off)
  254. if err != nil {
  255. return nil, nil, err
  256. }
  257. }
  258. for i := 0; i < len(dns.Answer); i++ {
  259. dns.Answer[i], off, err = UnpackRR(msg, off)
  260. if err != nil {
  261. return nil, nil, err
  262. }
  263. }
  264. for i := 0; i < len(dns.Ns); i++ {
  265. dns.Ns[i], off, err = UnpackRR(msg, off)
  266. if err != nil {
  267. return nil, nil, err
  268. }
  269. }
  270. for i := 0; i < len(dns.Extra); i++ {
  271. tsigoff = off
  272. dns.Extra[i], off, err = UnpackRR(msg, off)
  273. if err != nil {
  274. return nil, nil, err
  275. }
  276. if dns.Extra[i].Header().Rrtype == TypeTSIG {
  277. rr = dns.Extra[i].(*TSIG)
  278. // Adjust Arcount.
  279. arcount, _ := unpackUint16(msg, 10)
  280. msg[10], msg[11] = packUint16(arcount - 1)
  281. break
  282. }
  283. }
  284. if rr == nil {
  285. return nil, nil, ErrNoSig
  286. }
  287. return msg[:tsigoff], rr, nil
  288. }
  289. // Translate the TSIG time signed into a date. There is no
  290. // need for RFC1982 calculations as this date is 48 bits.
  291. func tsigTimeToString(t uint64) string {
  292. ti := time.Unix(int64(t), 0).UTC()
  293. return ti.Format("20060102150405")
  294. }