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