1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Flumotion Twisted credentials
24 """
25
26 import random
27
28 from flumotion.common import log, python
29 from twisted.cred import credentials
30 from zope.interface import implements
31
32 try:
33 import crypt
34 except ImportError:
35 from flumotion.extern import unixcrypt as crypt
36
37 __version__ = "$Rev: 7990 $"
38
39
41 """
42 I am your average username and password credentials.
43 """
44 implements(credentials.IUsernamePassword)
45
46 - def __init__(self, username, password=''):
49
52
53 IUsernamePassword = credentials.IUsernamePassword
54
55 IUsernameHashedPassword = credentials.IUsernameHashedPassword
56
57
59 """
60 I encapsulate a username and check crypted passwords.
61
62 This credential interface is used when a crypt password is received
63 from the party requesting authentication.
64 CredentialCheckers which check this kind of credential must store
65 the passwords in plaintext or crypt form.
66
67 @type username: C{str}
68 @ivar username: The username associated with these credentials.
69 """
70
72 """
73 Validate these credentials against the correct crypt password.
74
75 @param cryptPassword: The correct, crypt password against which to
76 check.
77
78 @return: a deferred which becomes, or a boolean indicating if the
79 password matches.
80 """
81
82
84 """
85 I take a username and a plaintext password.
86 I implement IUsernameCryptPassword.
87 """
88
89 implements(IUsernameCryptPassword)
90
91 - def __init__(self, username, password):
94
95 - def checkCryptPassword(self, cryptPassword):
96 """Check credentials against the given cryptPassword."""
97 salt = cryptPassword[:2]
98 encrypted = crypt.crypt(self.password, salt)
99 return encrypted == cryptPassword
100
101
103 """
104 I take a username and a crypt password.
105 When using me you should make sure the password was crypted with the
106 correct salt (which is stored in the crypt password backend of whatever
107 checker you use); otherwise your password may be a valid crypt, but
108 with a different salt.
109 I implement IUsernameCryptPassword.
110 """
111
112 implements(IUsernameCryptPassword)
113
114 - def __init__(self, username, cryptPassword=None):
117
119 """
120 Given the plaintext password and the salt,
121 set the correct cryptPassword.
122 """
123 assert len(salt) == 2
124
125 self.cryptPassword = crypt.crypt(password, salt)
126
128 """
129 Check credentials against the given cryptPassword.
130 """
131 return self.cryptPassword == cryptPassword
132
133
135 """
136 Respond to a given crypt challenge with our cryptPassword.
137 """
138 md = python.md5()
139 md.update(cryptPassword)
140 md.update(challenge)
141 return md.digest()
142
143
145 """
146 Take a string of bytes, and return a string of two-digit hex values.
147 """
148 l = []
149 for c in data:
150 l.append("%02x" % ord(c))
151 return "".join(l)
152
153
154
155
157 """
158 I return some random data.
159 """
160 crap = ''
161 for x in range(random.randrange(15, 25)):
162 crap = crap + chr(random.randint(65, 90) + x - x)
163 crap = python.md5(crap).digest()
164 return crap
165
166
168 """
169 I take a username.
170
171 Authenticator will give me a salt and challenge me.
172 Requester will respond to the challenge.
173 At that point I'm ready to be used by a checker.
174 The response function used is
175 L{flumotion.twisted.credentials.cryptRespond()}
176
177 I implement IUsernameCryptPassword.
178 """
179
180 implements(IUsernameCryptPassword)
181
183 self.username = username
184 self.salt = None
185 self.challenge = None
186 self.response = None
187
189 """
190 I encode a given plaintext password using the salt, and respond
191 to the challenge.
192 """
193 assert self.salt
194 assert self.challenge
195 assert len(self.salt) == 2
196 cryptPassword = crypt.crypt(password, self.salt)
197 self.response = cryptRespond(self.challenge, cryptPassword)
198
200 """
201 Check credentials against the given cryptPassword.
202 """
203 if not self.response:
204 return False
205
206 expected = cryptRespond(self.challenge, cryptPassword)
207 return self.response == expected
208
209
210 -class IToken(credentials.ICredentials):
211 """I encapsulate a token.
212
213 This credential is used when a token is received from the
214 party requesting authentication.
215
216 @type token: C{str}
217 @ivar token: The token associated with these credentials.
218 """
219
220
226
227
229 """I encapsulate HTTP GET request arguments.
230
231 This credential is used when authentication
232 depend on HTTP request arguments.
233
234 @type arguments: C{dict}
235 @ivar arguments: The HTTP request arguments.
236 """
237
238
244
245
247 """
248 I encapsulate a username and check SHA-256 passwords.
249
250 This credential interface is used when a SHA-256 algorithm is used
251 on the password by the party requesting authentication..
252 CredentialCheckers which check this kind of credential must store
253 the passwords in plaintext or SHA-256 form.
254
255 @type username: C{str}
256 @ivar username: The username associated with these credentials.
257 """
258
260 """
261 Validate these credentials against the correct SHA-256 password.
262
263 @param sha256Password: The correct SHA-256 password against which to
264 check.
265
266 @return: a deferred which becomes, or a boolean indicating if the
267 password matches.
268 """
269
270
271
272
273
275 """
276 I take a username.
277
278 Authenticator will give me a salt and challenge me.
279 Requester will respond to the challenge.
280 At that point I'm ready to be used by a checker.
281 The response function used is
282 L{flumotion.twisted.credentials.cryptRespond()}
283
284 I implement IUsernameSha256Password.
285 """
286
287 implements(IUsernameSha256Password)
288
290 self.username = username
291 self.salt = None
292 self.challenge = None
293 self.response = None
294
296 """
297 I encode a given plaintext password using the salt, and respond
298 to the challenge.
299 """
300 assert self.salt
301 assert self.challenge
302 from Crypto.Hash import SHA256
303 hasher = SHA256.new()
304 hasher.update(self.salt)
305 hasher.update(password)
306 sha256Password = self.salt + dataToHex(hasher.digest())
307 self.response = cryptRespond(self.challenge, sha256Password)
308
310 """
311 Check credentials against the given sha256Password.
312 """
313 if not self.response:
314 return False
315
316 expected = cryptRespond(self.challenge, sha256Password)
317 return self.response == expected
318
319
321 _algorithm = "MD5"
322
324 self.username = username
325 self.nonce = None
326 self.method = None
327 self.uri = None
328
329 self.qop = None
330 self.cnonce = None
331 self.ncvalue = None
332
333 self.response = None
334
336 expectedResponse = self._calculateRequestDigest(
337 self.username, ha1, self.nonce, self.cnonce,
338 self.method, self.uri, self.ncvalue, self.qop)
339
340 self.debug(
341 "Attempting to check calculated response %s against "
342 " provided response %r", expectedResponse, self.response)
343 self.debug("Username %s, nonce %s, method %s, uri %s, qop %s, "
344 "cnonce %s, ncvalue %s", self.username, self.nonce,
345 self.method, self.uri, self.qop, self.cnonce,
346 self.ncvalue)
347 self.debug("Using H(A1): %s", ha1)
348
349 if not self.response:
350 return False
351
352 return self.response == expectedResponse
353
355 """
356 Calculate H(A1) as from specification (RFC2617) section 3.2.2, given
357 the initial hash H(username:realm:passwd), hex-encoded.
358
359 This basically applies the second-level hashing for MD5-sess, if
360 required.
361 """
362 if self._algorithm == 'MD5':
363 return ha1
364 elif self._algorithm == 'MD5-sess':
365 HA1 = ha1.decode('hex')
366
367 m = python.md5()
368 m.update(HA1)
369 m.update(':')
370 m.update(nonce)
371 m.update(':')
372 m.update(cnonce)
373 return m.digest().encode('hex')
374 else:
375 raise NotImplementedError("Unimplemented algorithm")
376
385
406