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