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