source: trunk/server/lib/dnspython/dns/tsig.py

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 7.7 KB
Line 
1# Copyright (C) 2001-2007, 2009, 2010 Nominum, Inc.
2#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose with or without fee is hereby granted,
5# provided that the above copyright notice and this permission notice
6# appear in all copies.
7#
8# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16"""DNS TSIG support."""
17
18import hmac
19import struct
20import sys
21
22import dns.exception
23import dns.hash
24import dns.rdataclass
25import dns.name
26
27class BadTime(dns.exception.DNSException):
28 """Raised if the current time is not within the TSIG's validity time."""
29 pass
30
31class BadSignature(dns.exception.DNSException):
32 """Raised if the TSIG signature fails to verify."""
33 pass
34
35class PeerError(dns.exception.DNSException):
36 """Base class for all TSIG errors generated by the remote peer"""
37 pass
38
39class PeerBadKey(PeerError):
40 """Raised if the peer didn't know the key we used"""
41 pass
42
43class PeerBadSignature(PeerError):
44 """Raised if the peer didn't like the signature we sent"""
45 pass
46
47class PeerBadTime(PeerError):
48 """Raised if the peer didn't like the time we sent"""
49 pass
50
51class PeerBadTruncation(PeerError):
52 """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
53 pass
54
55# TSIG Algorithms
56
57HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT")
58HMAC_SHA1 = dns.name.from_text("hmac-sha1")
59HMAC_SHA224 = dns.name.from_text("hmac-sha224")
60HMAC_SHA256 = dns.name.from_text("hmac-sha256")
61HMAC_SHA384 = dns.name.from_text("hmac-sha384")
62HMAC_SHA512 = dns.name.from_text("hmac-sha512")
63
64default_algorithm = HMAC_MD5
65
66BADSIG = 16
67BADKEY = 17
68BADTIME = 18
69BADTRUNC = 22
70
71def 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
122def 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
128def 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
183def _maybe_add_hash(tsig_alg, hash_alg):
184 try:
185 _hashes[tsig_alg] = dns.hash.get(hash_alg)
186 except KeyError:
187 pass
188
189def _setup_hashes():
190 global _hashes
191 _hashes = {}
192 _maybe_add_hash(HMAC_SHA224, 'SHA224')
193 _maybe_add_hash(HMAC_SHA256, 'SHA256')
194 _maybe_add_hash(HMAC_SHA384, 'SHA384')
195 _maybe_add_hash(HMAC_SHA512, 'SHA512')
196 _maybe_add_hash(HMAC_SHA1, 'SHA1')
197 _maybe_add_hash(HMAC_MD5, 'MD5')
198
199def get_algorithm(algorithm):
200 """Returns the wire format string and the hash module to use for the
201 specified TSIG algorithm
202
203 @rtype: (string, hash constructor)
204 @raises NotImplementedError: I{algorithm} is not supported
205 """
206
207 global _hashes
208 if _hashes is None:
209 _setup_hashes()
210
211 if isinstance(algorithm, (str, unicode)):
212 algorithm = dns.name.from_text(algorithm)
213
214 if sys.hexversion < 0x02050200 and \
215 (algorithm == HMAC_SHA384 or algorithm == HMAC_SHA512):
216 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
217 " requires Python 2.5.2 or later")
218
219 try:
220 return (algorithm.to_digestable(), _hashes[algorithm])
221 except KeyError:
222 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
223 " is not supported")
Note: See TracBrowser for help on using the repository browser.