Package dns :: Module tsig
[hide private]
[frames] | no frames]

Source Code for Module dns.tsig

  1  # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. 
  2  # 
  3  # Permission to use, copy, modify, and distribute this software and its 
  4  # documentation for any purpose with or without fee is hereby granted, 
  5  # provided that the above copyright notice and this permission notice 
  6  # appear in all copies. 
  7  # 
  8  # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 
  9  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
 10  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 
 11  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 12  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
 13  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 
 14  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 15   
 16  """DNS TSIG support.""" 
 17   
 18  import hmac 
 19  import struct 
 20   
 21  import dns.exception 
 22  import dns.hash 
 23  import dns.rdataclass 
 24  import dns.name 
 25  from ._compat import long, string_types, text_type 
 26   
27 -class BadTime(dns.exception.DNSException):
28 29 """The current time is not within the TSIG's validity time."""
30 31
32 -class BadSignature(dns.exception.DNSException):
33 34 """The TSIG signature fails to verify."""
35 36
37 -class PeerError(dns.exception.DNSException):
38 39 """Base class for all TSIG errors generated by the remote peer"""
40 41
42 -class PeerBadKey(PeerError):
43 44 """The peer didn't know the key we used"""
45 46
47 -class PeerBadSignature(PeerError):
48 49 """The peer didn't like the signature we sent"""
50 51
52 -class PeerBadTime(PeerError):
53 54 """The peer didn't like the time we sent"""
55 56
57 -class PeerBadTruncation(PeerError):
58 59 """The peer didn't like amount of truncation in the TSIG we sent"""
60 61 # TSIG Algorithms 62 63 HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") 64 HMAC_SHA1 = dns.name.from_text("hmac-sha1") 65 HMAC_SHA224 = dns.name.from_text("hmac-sha224") 66 HMAC_SHA256 = dns.name.from_text("hmac-sha256") 67 HMAC_SHA384 = dns.name.from_text("hmac-sha384") 68 HMAC_SHA512 = dns.name.from_text("hmac-sha512") 69 70 _hashes = { 71 HMAC_SHA224: 'SHA224', 72 HMAC_SHA256: 'SHA256', 73 HMAC_SHA384: 'SHA384', 74 HMAC_SHA512: 'SHA512', 75 HMAC_SHA1: 'SHA1', 76 HMAC_MD5: 'MD5', 77 } 78 79 default_algorithm = HMAC_MD5 80 81 BADSIG = 16 82 BADKEY = 17 83 BADTIME = 18 84 BADTRUNC = 22 85 86
87 -def sign(wire, keyname, secret, time, fudge, original_id, error, 88 other_data, request_mac, ctx=None, multi=False, first=True, 89 algorithm=default_algorithm):
90 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata 91 for the input parameters, the HMAC MAC calculated by applying the 92 TSIG signature algorithm, and the TSIG digest context. 93 @rtype: (string, string, hmac.HMAC object) 94 @raises ValueError: I{other_data} is too long 95 @raises NotImplementedError: I{algorithm} is not supported 96 """ 97 98 if isinstance(other_data, text_type): 99 other_data = other_data.encode() 100 (algorithm_name, digestmod) = get_algorithm(algorithm) 101 if first: 102 ctx = hmac.new(secret, digestmod=digestmod) 103 ml = len(request_mac) 104 if ml > 0: 105 ctx.update(struct.pack('!H', ml)) 106 ctx.update(request_mac) 107 id = struct.pack('!H', original_id) 108 ctx.update(id) 109 ctx.update(wire[2:]) 110 if first: 111 ctx.update(keyname.to_digestable()) 112 ctx.update(struct.pack('!H', dns.rdataclass.ANY)) 113 ctx.update(struct.pack('!I', 0)) 114 long_time = time + long(0) 115 upper_time = (long_time >> 32) & long(0xffff) 116 lower_time = long_time & long(0xffffffff) 117 time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) 118 pre_mac = algorithm_name + time_mac 119 ol = len(other_data) 120 if ol > 65535: 121 raise ValueError('TSIG Other Data is > 65535 bytes') 122 post_mac = struct.pack('!HH', error, ol) + other_data 123 if first: 124 ctx.update(pre_mac) 125 ctx.update(post_mac) 126 else: 127 ctx.update(time_mac) 128 mac = ctx.digest() 129 mpack = struct.pack('!H', len(mac)) 130 tsig_rdata = pre_mac + mpack + mac + id + post_mac 131 if multi: 132 ctx = hmac.new(secret, digestmod=digestmod) 133 ml = len(mac) 134 ctx.update(struct.pack('!H', ml)) 135 ctx.update(mac) 136 else: 137 ctx = None 138 return (tsig_rdata, mac, ctx)
139 140
141 -def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, 142 other_data, request_mac, ctx=None, multi=False, first=True, 143 algorithm=default_algorithm):
144 return sign(wire, keyname, secret, time, fudge, original_id, error, 145 other_data, request_mac, ctx, multi, first, algorithm)
146 147
148 -def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, 149 tsig_rdlen, ctx=None, multi=False, first=True):
150 """Validate the specified TSIG rdata against the other input parameters. 151 152 @raises FormError: The TSIG is badly formed. 153 @raises BadTime: There is too much time skew between the client and the 154 server. 155 @raises BadSignature: The TSIG signature did not validate 156 @rtype: hmac.HMAC object""" 157 158 (adcount,) = struct.unpack("!H", wire[10:12]) 159 if adcount == 0: 160 raise dns.exception.FormError 161 adcount -= 1 162 new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] 163 current = tsig_rdata 164 (aname, used) = dns.name.from_wire(wire, current) 165 current = current + used 166 (upper_time, lower_time, fudge, mac_size) = \ 167 struct.unpack("!HIHH", wire[current:current + 10]) 168 time = ((upper_time + long(0)) << 32) + (lower_time + long(0)) 169 current += 10 170 mac = wire[current:current + mac_size] 171 current += mac_size 172 (original_id, error, other_size) = \ 173 struct.unpack("!HHH", wire[current:current + 6]) 174 current += 6 175 other_data = wire[current:current + other_size] 176 current += other_size 177 if current != tsig_rdata + tsig_rdlen: 178 raise dns.exception.FormError 179 if error != 0: 180 if error == BADSIG: 181 raise PeerBadSignature 182 elif error == BADKEY: 183 raise PeerBadKey 184 elif error == BADTIME: 185 raise PeerBadTime 186 elif error == BADTRUNC: 187 raise PeerBadTruncation 188 else: 189 raise PeerError('unknown TSIG error code %d' % error) 190 time_low = time - fudge 191 time_high = time + fudge 192 if now < time_low or now > time_high: 193 raise BadTime 194 (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge, 195 original_id, error, other_data, 196 request_mac, ctx, multi, first, aname) 197 if our_mac != mac: 198 raise BadSignature 199 return ctx
200 201
202 -def get_algorithm(algorithm):
203 """Returns the wire format string and the hash module to use for the 204 specified TSIG algorithm 205 206 @rtype: (string, hash constructor) 207 @raises NotImplementedError: I{algorithm} is not supported 208 """ 209 210 if isinstance(algorithm, string_types): 211 algorithm = dns.name.from_text(algorithm) 212 213 try: 214 return (algorithm.to_digestable(), dns.hash.hashes[_hashes[algorithm]]) 215 except KeyError: 216 raise NotImplementedError("TSIG algorithm " + str(algorithm) + 217 " is not supported")
218 219
220 -def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen):
221 """Return the tsig algorithm for the specified tsig_rdata 222 @raises FormError: The TSIG is badly formed. 223 """ 224 current = tsig_rdata 225 (aname, used) = dns.name.from_wire(wire, current) 226 current = current + used 227 (upper_time, lower_time, fudge, mac_size) = \ 228 struct.unpack("!HIHH", wire[current:current + 10]) 229 current += 10 230 mac = wire[current:current + mac_size] 231 current += mac_size 232 if current > tsig_rdata + tsig_rdlen: 233 raise dns.exception.FormError 234 return (aname, mac)
235