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