dnspython  1.16.0
About: dnspython is a DNS toolkit (for Python 2.x) that supports almost all record types.
  Fossies Dox: dnspython-1.16.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

tsig.py
Go to the documentation of this file.
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 
30 
31  """The current time is not within the TSIG's validity time."""
32 
33 
35 
36  """The TSIG signature fails to verify."""
37 
38 
40 
41  """Base class for all TSIG errors generated by the remote peer"""
42 
43 
45 
46  """The peer didn't know the key we used"""
47 
48 
50 
51  """The peer didn't like the signature we sent"""
52 
53 
55 
56  """The peer didn't like the time we sent"""
57 
58 
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:
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:
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:
236  return (aname, mac)
dns.exception.FormError
Definition: exception.py:109
dns.tsig.sign
def sign(wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx=None, multi=False, first=True, algorithm=default_algorithm)
Definition: tsig.py:89
dns.name.from_text
def from_text(text, origin=root, idna_codec=None)
Definition: name.py:873
dns.rdataclass
Definition: rdataclass.py:1
dns.tsig.PeerBadKey
Definition: tsig.py:44
dns.tsig.BadTime
Definition: tsig.py:29
dns.exception.DNSException
Definition: exception.py:24
dns.tsig.PeerBadTruncation
Definition: tsig.py:59
dns.name.from_wire
def from_wire(message, current)
Definition: name.py:945
dns.tsig.PeerError
Definition: tsig.py:39
dns.tsig.hmac_md5
def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx=None, multi=False, first=True, algorithm=default_algorithm)
Definition: tsig.py:143
dns.name
Definition: name.py:1
dns.tsig.PeerBadTime
Definition: tsig.py:54
dns.tsig.BadSignature
Definition: tsig.py:34
dns.tsig.validate
def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, tsig_rdlen, ctx=None, multi=False, first=True)
Definition: tsig.py:150
dns.tsig.get_algorithm_and_mac
def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen)
Definition: tsig.py:222
dns.tsig.get_algorithm
def get_algorithm(algorithm)
Definition: tsig.py:204
dns._compat.long
long
Definition: _compat.py:10
dns.exception
Definition: exception.py:1
dns.tsig.PeerBadSignature
Definition: tsig.py:49