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