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