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