s3:lib/events: s/EVENT_FD/TEVENT_FD
[Samba/gebeck_regimport.git] / lib / dnspython / dns / tsig.py
blob63b925afbb330218a32b3d06dfb07a144282db60
1 # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
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.
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.
16 """DNS TSIG support."""
18 import hmac
19 import struct
20 import sys
22 import dns.exception
23 import dns.hash
24 import dns.rdataclass
25 import dns.name
27 class BadTime(dns.exception.DNSException):
28 """Raised if the current time is not within the TSIG's validity time."""
29 pass
31 class BadSignature(dns.exception.DNSException):
32 """Raised if the TSIG signature fails to verify."""
33 pass
35 class PeerError(dns.exception.DNSException):
36 """Base class for all TSIG errors generated by the remote peer"""
37 pass
39 class PeerBadKey(PeerError):
40 """Raised if the peer didn't know the key we used"""
41 pass
43 class PeerBadSignature(PeerError):
44 """Raised if the peer didn't like the signature we sent"""
45 pass
47 class PeerBadTime(PeerError):
48 """Raised if the peer didn't like the time we sent"""
49 pass
51 class PeerBadTruncation(PeerError):
52 """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
53 pass
55 # TSIG Algorithms
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")
64 default_algorithm = HMAC_MD5
66 BADSIG = 16
67 BADKEY = 17
68 BADTIME = 18
69 BADTRUNC = 22
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 """
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)
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)
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.
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"""
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
181 _hashes = None
183 def _maybe_add_hash(tsig_alg, hash_alg):
184 try:
185 _hashes[tsig_alg] = dns.hash.get(hash_alg)
186 except KeyError:
187 pass
189 def _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')
199 def get_algorithm(algorithm):
200 """Returns the wire format string and the hash module to use for the
201 specified TSIG algorithm
203 @rtype: (string, hash constructor)
204 @raises NotImplementedError: I{algorithm} is not supported
207 global _hashes
208 if _hashes is None:
209 _setup_hashes()
211 if isinstance(algorithm, (str, unicode)):
212 algorithm = dns.name.from_text(algorithm)
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")
219 try:
220 return (algorithm.to_digestable(), _hashes[algorithm])
221 except KeyError:
222 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
223 " is not supported")