1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS TSIG support."""
17
18 import hmac
19 import struct
20
21 import dns.exception
22 import dns.rdataclass
23 import dns.name
24
25 -class BadTime(dns.exception.DNSException):
26 """Raised if the current time is not within the TSIG's validity time."""
27 pass
28
30 """Raised if the TSIG signature fails to verify."""
31 pass
32
34 """Base class for all TSIG errors generated by the remote peer"""
35 pass
36
38 """Raised if the peer didn't know the key we used"""
39 pass
40
42 """Raised if the peer didn't like the signature we sent"""
43 pass
44
46 """Raised if the peer didn't like the time we sent"""
47 pass
48
50 """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
51 pass
52
53 default_algorithm = "HMAC-MD5.SIG-ALG.REG.INT"
54
55 BADSIG = 16
56 BADKEY = 17
57 BADTIME = 18
58 BADTRUNC = 22
59
60 -def sign(wire, keyname, secret, time, fudge, original_id, error,
61 other_data, request_mac, ctx=None, multi=False, first=True,
62 algorithm=default_algorithm):
63 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
64 for the input parameters, the HMAC MAC calculated by applying the
65 TSIG signature algorithm, and the TSIG digest context.
66 @rtype: (string, string, hmac.HMAC object)
67 @raises ValueError: I{other_data} is too long
68 @raises NotImplementedError: I{algorithm} is not supported
69 """
70
71 (algorithm_name, digestmod) = get_algorithm(algorithm)
72 if first:
73 ctx = hmac.new(secret, digestmod=digestmod)
74 ml = len(request_mac)
75 if ml > 0:
76 ctx.update(struct.pack('!H', ml))
77 ctx.update(request_mac)
78 id = struct.pack('!H', original_id)
79 ctx.update(id)
80 ctx.update(wire[2:])
81 if first:
82 ctx.update(keyname.to_digestable())
83 ctx.update(struct.pack('!H', dns.rdataclass.ANY))
84 ctx.update(struct.pack('!I', 0))
85 long_time = time + 0L
86 upper_time = (long_time >> 32) & 0xffffL
87 lower_time = long_time & 0xffffffffL
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 + 0L) << 32) + (lower_time + 0L)
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
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 try:
180 import hashlib
181 hashes[dns.name.from_text('hmac-sha224')] = hashlib.sha224
182 hashes[dns.name.from_text('hmac-sha256')] = hashlib.sha256
183 hashes[dns.name.from_text('hmac-sha384')] = hashlib.sha384
184 hashes[dns.name.from_text('hmac-sha512')] = hashlib.sha512
185 hashes[dns.name.from_text('hmac-sha1')] = hashlib.sha1
186 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = hashlib.md5
187
188 import sys
189 if sys.hexversion < 0x02050000:
190
191
192
193 class HashlibWrapper:
194 def __init__(self, basehash):
195 self.basehash = basehash
196 self.digest_size = self.basehash().digest_size
197
198 def new(self, *args, **kwargs):
199 return self.basehash(*args, **kwargs)
200
201 for name in hashes:
202 hashes[name] = HashlibWrapper(hashes[name])
203
204 except ImportError:
205 import md5, sha
206 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = md5.md5
207 hashes[dns.name.from_text('hmac-sha1')] = sha.sha
208
209 if isinstance(algorithm, (str, unicode)):
210 algorithm = dns.name.from_text(algorithm)
211
212 if algorithm in hashes:
213 return (algorithm.to_digestable(), hashes[algorithm])
214
215 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
216 " is not supported")
217