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
29 -class BadTime(dns.exception.DNSException):
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
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 default_algorithm = HMAC_MD5
73
74 BADSIG = 16
75 BADKEY = 17
76 BADTIME = 18
77 BADTRUNC = 22
78
79
80 -def sign(wire, keyname, secret, time, fudge, original_id, error,
81 other_data, request_mac, ctx=None, multi=False, first=True,
82 algorithm=default_algorithm):
83 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
84 for the input parameters, the HMAC MAC calculated by applying the
85 TSIG signature algorithm, and the TSIG digest context.
86 @rtype: (string, string, hmac.HMAC object)
87 @raises ValueError: I{other_data} is too long
88 @raises NotImplementedError: I{algorithm} is not supported
89 """
90
91 (algorithm_name, digestmod) = get_algorithm(algorithm)
92 if first:
93 ctx = hmac.new(secret, digestmod=digestmod)
94 ml = len(request_mac)
95 if ml > 0:
96 ctx.update(struct.pack('!H', ml))
97 ctx.update(request_mac)
98 id = struct.pack('!H', original_id)
99 ctx.update(id)
100 ctx.update(wire[2:])
101 if first:
102 ctx.update(keyname.to_digestable())
103 ctx.update(struct.pack('!H', dns.rdataclass.ANY))
104 ctx.update(struct.pack('!I', 0))
105 long_time = time + long(0)
106 upper_time = (long_time >> 32) & long(0xffff)
107 lower_time = long_time & long(0xffffffff)
108 time_mac = struct.pack('!HIH', upper_time, lower_time, fudge)
109 pre_mac = algorithm_name + time_mac
110 ol = len(other_data)
111 if ol > 65535:
112 raise ValueError('TSIG Other Data is > 65535 bytes')
113 post_mac = struct.pack('!HH', error, ol) + other_data
114 if first:
115 ctx.update(pre_mac)
116 ctx.update(post_mac)
117 else:
118 ctx.update(time_mac)
119 mac = ctx.digest()
120 mpack = struct.pack('!H', len(mac))
121 tsig_rdata = pre_mac + mpack + mac + id + post_mac
122 if multi:
123 ctx = hmac.new(secret, digestmod=digestmod)
124 ml = len(mac)
125 ctx.update(struct.pack('!H', ml))
126 ctx.update(mac)
127 else:
128 ctx = None
129 return (tsig_rdata, mac, ctx)
130
131
132 -def hmac_md5(wire, keyname, secret, time, fudge, original_id, error,
133 other_data, request_mac, ctx=None, multi=False, first=True,
134 algorithm=default_algorithm):
135 return sign(wire, keyname, secret, time, fudge, original_id, error,
136 other_data, request_mac, ctx, multi, first, algorithm)
137
138
139 -def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata,
140 tsig_rdlen, ctx=None, multi=False, first=True):
141 """Validate the specified TSIG rdata against the other input parameters.
142
143 @raises FormError: The TSIG is badly formed.
144 @raises BadTime: There is too much time skew between the client and the
145 server.
146 @raises BadSignature: The TSIG signature did not validate
147 @rtype: hmac.HMAC object"""
148
149 (adcount,) = struct.unpack("!H", wire[10:12])
150 if adcount == 0:
151 raise dns.exception.FormError
152 adcount -= 1
153 new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start]
154 current = tsig_rdata
155 (aname, used) = dns.name.from_wire(wire, current)
156 current = current + used
157 (upper_time, lower_time, fudge, mac_size) = \
158 struct.unpack("!HIHH", wire[current:current + 10])
159 time = ((upper_time + long(0)) << 32) + (lower_time + long(0))
160 current += 10
161 mac = wire[current:current + mac_size]
162 current += mac_size
163 (original_id, error, other_size) = \
164 struct.unpack("!HHH", wire[current:current + 6])
165 current += 6
166 other_data = wire[current:current + other_size]
167 current += other_size
168 if current != tsig_rdata + tsig_rdlen:
169 raise dns.exception.FormError
170 if error != 0:
171 if error == BADSIG:
172 raise PeerBadSignature
173 elif error == BADKEY:
174 raise PeerBadKey
175 elif error == BADTIME:
176 raise PeerBadTime
177 elif error == BADTRUNC:
178 raise PeerBadTruncation
179 else:
180 raise PeerError('unknown TSIG error code %d' % error)
181 time_low = time - fudge
182 time_high = time + fudge
183 if now < time_low or now > time_high:
184 raise BadTime
185 (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge,
186 original_id, error, other_data,
187 request_mac, ctx, multi, first, aname)
188 if (our_mac != mac):
189 raise BadSignature
190 return ctx
191
192
209
210
212 """Return the tsig algorithm for the specified tsig_rdata
213 @raises FormError: The TSIG is badly formed.
214 """
215 current = tsig_rdata
216 (aname, used) = dns.name.from_wire(wire, current)
217 current = current + used
218 (upper_time, lower_time, fudge, mac_size) = \
219 struct.unpack("!HIHH", wire[current:current + 10])
220 current += 10
221 mac = wire[current:current + mac_size]
222 current += mac_size
223 if current > tsig_rdata + tsig_rdlen:
224 raise dns.exception.FormError
225 return (aname, mac)
226