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

Source Code for Module dns.tsig

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