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   
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 default_algorithm = HMAC_MD5 73 74 BADSIG = 16 75 BADKEY = 17 76 BADTIME = 18 77 BADTRUNC = 22 78 79
80 -def sign(wire, keyname, secret, time, fudge, original_id, error, 81 other_data, request_mac, ctx=None, multi=False, first=True, 82 algorithm=default_algorithm):
83 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata 84 for the input parameters, the HMAC MAC calculated by applying the 85 TSIG signature algorithm, and the TSIG digest context. 86 @rtype: (string, string, hmac.HMAC object) 87 @raises ValueError: I{other_data} is too long 88 @raises NotImplementedError: I{algorithm} is not supported 89 """ 90 91 (algorithm_name, digestmod) = get_algorithm(algorithm) 92 if first: 93 ctx = hmac.new(secret, digestmod=digestmod) 94 ml = len(request_mac) 95 if ml > 0: 96 ctx.update(struct.pack('!H', ml)) 97 ctx.update(request_mac) 98 id = struct.pack('!H', original_id) 99 ctx.update(id) 100 ctx.update(wire[2:]) 101 if first: 102 ctx.update(keyname.to_digestable()) 103 ctx.update(struct.pack('!H', dns.rdataclass.ANY)) 104 ctx.update(struct.pack('!I', 0)) 105 long_time = time + long(0) 106 upper_time = (long_time >> 32) & long(0xffff) 107 lower_time = long_time & long(0xffffffff) 108 time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) 109 pre_mac = algorithm_name + time_mac 110 ol = len(other_data) 111 if ol > 65535: 112 raise ValueError('TSIG Other Data is > 65535 bytes') 113 post_mac = struct.pack('!HH', error, ol) + other_data 114 if first: 115 ctx.update(pre_mac) 116 ctx.update(post_mac) 117 else: 118 ctx.update(time_mac) 119 mac = ctx.digest() 120 mpack = struct.pack('!H', len(mac)) 121 tsig_rdata = pre_mac + mpack + mac + id + post_mac 122 if multi: 123 ctx = hmac.new(secret, digestmod=digestmod) 124 ml = len(mac) 125 ctx.update(struct.pack('!H', ml)) 126 ctx.update(mac) 127 else: 128 ctx = None 129 return (tsig_rdata, mac, ctx)
130 131
132 -def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, 133 other_data, request_mac, ctx=None, multi=False, first=True, 134 algorithm=default_algorithm):
135 return sign(wire, keyname, secret, time, fudge, original_id, error, 136 other_data, request_mac, ctx, multi, first, algorithm)
137 138
139 -def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, 140 tsig_rdlen, ctx=None, multi=False, first=True):
141 """Validate the specified TSIG rdata against the other input parameters. 142 143 @raises FormError: The TSIG is badly formed. 144 @raises BadTime: There is too much time skew between the client and the 145 server. 146 @raises BadSignature: The TSIG signature did not validate 147 @rtype: hmac.HMAC object""" 148 149 (adcount,) = struct.unpack("!H", wire[10:12]) 150 if adcount == 0: 151 raise dns.exception.FormError 152 adcount -= 1 153 new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] 154 current = tsig_rdata 155 (aname, used) = dns.name.from_wire(wire, current) 156 current = current + used 157 (upper_time, lower_time, fudge, mac_size) = \ 158 struct.unpack("!HIHH", wire[current:current + 10]) 159 time = ((upper_time + long(0)) << 32) + (lower_time + long(0)) 160 current += 10 161 mac = wire[current:current + mac_size] 162 current += mac_size 163 (original_id, error, other_size) = \ 164 struct.unpack("!HHH", wire[current:current + 6]) 165 current += 6 166 other_data = wire[current:current + other_size] 167 current += other_size 168 if current != tsig_rdata + tsig_rdlen: 169 raise dns.exception.FormError 170 if error != 0: 171 if error == BADSIG: 172 raise PeerBadSignature 173 elif error == BADKEY: 174 raise PeerBadKey 175 elif error == BADTIME: 176 raise PeerBadTime 177 elif error == BADTRUNC: 178 raise PeerBadTruncation 179 else: 180 raise PeerError('unknown TSIG error code %d' % error) 181 time_low = time - fudge 182 time_high = time + fudge 183 if now < time_low or now > time_high: 184 raise BadTime 185 (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge, 186 original_id, error, other_data, 187 request_mac, ctx, multi, first, aname) 188 if (our_mac != mac): 189 raise BadSignature 190 return ctx
191 192
193 -def get_algorithm(algorithm):
194 """Returns the wire format string and the hash module to use for the 195 specified TSIG algorithm 196 197 @rtype: (string, hash constructor) 198 @raises NotImplementedError: I{algorithm} is not supported 199 """ 200 201 if isinstance(algorithm, string_types): 202 algorithm = dns.name.from_text(algorithm) 203 204 try: 205 return (algorithm.to_digestable(), dns.hash.hashes[algorithm]) 206 except KeyError: 207 raise NotImplementedError("TSIG algorithm " + str(algorithm) + 208 " is not supported")
209 210
211 -def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen):
212 """Return the tsig algorithm for the specified tsig_rdata 213 @raises FormError: The TSIG is badly formed. 214 """ 215 current = tsig_rdata 216 (aname, used) = dns.name.from_wire(wire, current) 217 current = current + used 218 (upper_time, lower_time, fudge, mac_size) = \ 219 struct.unpack("!HIHH", wire[current:current + 10]) 220 current += 10 221 mac = wire[current:current + mac_size] 222 current += mac_size 223 if current > tsig_rdata + tsig_rdlen: 224 raise dns.exception.FormError 225 return (aname, mac)
226