s3-auth: Added a function to get the server_info from the system user.
[Samba/ekacnet.git] / lib / dnspython / dns / tsig.py
blobb4deeca859dcaac594acd4733326c17b64f5a4c7
1 # Copyright (C) 2001-2007, 2009, 2010 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
21 import dns.exception
22 import dns.rdataclass
23 import dns.name
25 class BadTime(dns.exception.DNSException):
26 """Raised if the current time is not within the TSIG's validity time."""
27 pass
29 class BadSignature(dns.exception.DNSException):
30 """Raised if the TSIG signature fails to verify."""
31 pass
33 class PeerError(dns.exception.DNSException):
34 """Base class for all TSIG errors generated by the remote peer"""
35 pass
37 class PeerBadKey(PeerError):
38 """Raised if the peer didn't know the key we used"""
39 pass
41 class PeerBadSignature(PeerError):
42 """Raised if the peer didn't like the signature we sent"""
43 pass
45 class PeerBadTime(PeerError):
46 """Raised if the peer didn't like the time we sent"""
47 pass
49 class PeerBadTruncation(PeerError):
50 """Raised if the peer didn't like amount of truncation in the TSIG we sent"""
51 pass
53 default_algorithm = "HMAC-MD5.SIG-ALG.REG.INT"
55 BADSIG = 16
56 BADKEY = 17
57 BADTIME = 18
58 BADTRUNC = 22
60 def sign(wire, keyname, secret, time, fudge, original_id, error,
61 other_data, request_mac, ctx=None, multi=False, first=True,
62 algorithm=default_algorithm):
63 """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
64 for the input parameters, the HMAC MAC calculated by applying the
65 TSIG signature algorithm, and the TSIG digest context.
66 @rtype: (string, string, hmac.HMAC object)
67 @raises ValueError: I{other_data} is too long
68 @raises NotImplementedError: I{algorithm} is not supported
69 """
71 (algorithm_name, digestmod) = get_algorithm(algorithm)
72 if first:
73 ctx = hmac.new(secret, digestmod=digestmod)
74 ml = len(request_mac)
75 if ml > 0:
76 ctx.update(struct.pack('!H', ml))
77 ctx.update(request_mac)
78 id = struct.pack('!H', original_id)
79 ctx.update(id)
80 ctx.update(wire[2:])
81 if first:
82 ctx.update(keyname.to_digestable())
83 ctx.update(struct.pack('!H', dns.rdataclass.ANY))
84 ctx.update(struct.pack('!I', 0))
85 long_time = time + 0L
86 upper_time = (long_time >> 32) & 0xffffL
87 lower_time = long_time & 0xffffffffL
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)
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)
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.
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"""
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 + 0L) << 32) + (lower_time + 0L)
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
170 def get_algorithm(algorithm):
171 """Returns the wire format string and the hash module to use for the
172 specified TSIG algorithm
174 @rtype: (string, hash constructor)
175 @raises NotImplementedError: I{algorithm} is not supported
178 hashes = {}
179 try:
180 import hashlib
181 hashes[dns.name.from_text('hmac-sha224')] = hashlib.sha224
182 hashes[dns.name.from_text('hmac-sha256')] = hashlib.sha256
183 hashes[dns.name.from_text('hmac-sha384')] = hashlib.sha384
184 hashes[dns.name.from_text('hmac-sha512')] = hashlib.sha512
185 hashes[dns.name.from_text('hmac-sha1')] = hashlib.sha1
186 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = hashlib.md5
188 import sys
189 if sys.hexversion < 0x02050000:
190 # hashlib doesn't conform to PEP 247: API for
191 # Cryptographic Hash Functions, which hmac before python
192 # 2.5 requires, so add the necessary items.
193 class HashlibWrapper:
194 def __init__(self, basehash):
195 self.basehash = basehash
196 self.digest_size = self.basehash().digest_size
198 def new(self, *args, **kwargs):
199 return self.basehash(*args, **kwargs)
201 for name in hashes:
202 hashes[name] = HashlibWrapper(hashes[name])
204 except ImportError:
205 import md5, sha
206 hashes[dns.name.from_text('HMAC-MD5.SIG-ALG.REG.INT')] = md5.md5
207 hashes[dns.name.from_text('hmac-sha1')] = sha.sha
209 if isinstance(algorithm, (str, unicode)):
210 algorithm = dns.name.from_text(algorithm)
212 if algorithm in hashes:
213 return (algorithm.to_digestable(), hashes[algorithm])
215 raise NotImplementedError("TSIG algorithm " + str(algorithm) +
216 " is not supported")