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