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

Source Code for Module dns.tsig

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