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 md5
27
28 import random
29
30 from flumotion.common import log
31 from twisted.cred import credentials
32 from zope.interface import implements
33
34 try:
35 import crypt
36 except ImportError:
37 from flumotion.extern import unixcrypt as crypt
38
39 __version__ = "$Rev: 6558 $"
40
41
43 """
44 I am your average username and password credentials.
45 """
46 implements(credentials.IUsernamePassword)
47 - def __init__(self, username, password=''):
50
53
54 IUsernamePassword = credentials.IUsernamePassword
55
56 IUsernameHashedPassword = credentials.IUsernameHashedPassword
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
83 """
84 I take a username and a plaintext password.
85 I implement IUsernameCryptPassword.
86 """
87
88 implements(IUsernameCryptPassword)
89 - def __init__(self, username, password):
92
93 - def checkCryptPassword(self, cryptPassword):
94 """Check credentials against the given cryptPassword."""
95 salt = cryptPassword[:2]
96 encrypted = crypt.crypt(self.password, salt)
97 return encrypted == cryptPassword
98
100 """
101 I take a username and a crypt password.
102 When using me you should make sure the password was crypted with the
103 correct salt (which is stored in the crypt password backend of whatever
104 checker you use); otherwise your password may be a valid crypt, but
105 with a different salt.
106 I implement IUsernameCryptPassword.
107 """
108
109 implements(IUsernameCryptPassword)
110 - def __init__(self, username, cryptPassword=None):
113
115 """
116 Given the plaintext password and the salt,
117 set the correct cryptPassword.
118 """
119 assert len(salt) == 2
120
121 self.cryptPassword = crypt.crypt(password, salt)
122
124 """
125 Check credentials against the given cryptPassword.
126 """
127 return self.cryptPassword == cryptPassword
128
130 """
131 Respond to a given crypt challenge with our cryptPassword.
132 """
133 import md5
134 md = md5.new()
135 md.update(cryptPassword)
136 md.update(challenge)
137 return md.digest()
138
140 """
141 Take a string of bytes, and return a string of two-digit hex values.
142 """
143 l = []
144 for c in data:
145 l.append("%02x" % ord(c))
146 return "".join(l)
147
148
150 """
151 I return some random data.
152 """
153 crap = ''
154 for x in range(random.randrange(15,25)):
155 crap = crap + chr(random.randint(65,90) + x - x)
156 crap = md5.new(crap).digest()
157 return crap
158
160 """
161 I take a username.
162
163 Authenticator will give me a salt and challenge me.
164 Requester will respond to the challenge.
165 At that point I'm ready to be used by a checker.
166 The response function used is
167 L{flumotion.twisted.credentials.cryptRespond()}
168
169 I implement IUsernameCryptPassword.
170 """
171
172 implements(IUsernameCryptPassword)
173
175 self.username = username
176 self.salt = None
177 self.challenge = None
178 self.response = None
179
181 """
182 I encode a given plaintext password using the salt, and respond
183 to the challenge.
184 """
185 assert self.salt
186 assert self.challenge
187 assert len(self.salt) == 2
188 cryptPassword = crypt.crypt(password, self.salt)
189 self.response = cryptRespond(self.challenge, cryptPassword)
190
192 """
193 Check credentials against the given cryptPassword.
194 """
195 if not self.response:
196 return False
197
198 expected = cryptRespond(self.challenge, cryptPassword)
199 return self.response == expected
200
201 -class IToken(credentials.ICredentials):
202 """I encapsulate a token.
203
204 This credential is used when a token is received from the
205 party requesting authentication.
206
207 @type token: C{str}
208 @ivar token: The token associated with these credentials.
209 """
210
216
218 """
219 I encapsulate a username and check SHA-256 passwords.
220
221 This credential interface is used when a SHA-256 algorithm is used
222 on the password by the party requesting authentication..
223 CredentialCheckers which check this kind of credential must store
224 the passwords in plaintext or SHA-256 form.
225
226 @type username: C{str}
227 @ivar username: The username associated with these credentials.
228 """
229
231 """
232 Validate these credentials against the correct SHA-256 password.
233
234 @param sha256Password: The correct SHA-256 password against which to
235 check.
236
237 @return: a deferred which becomes, or a boolean indicating if the
238 password matches.
239 """
240
241
242
244 """
245 I take a username.
246
247 Authenticator will give me a salt and challenge me.
248 Requester will respond to the challenge.
249 At that point I'm ready to be used by a checker.
250 The response function used is
251 L{flumotion.twisted.credentials.cryptRespond()}
252
253 I implement IUsernameSha256Password.
254 """
255
256 implements(IUsernameSha256Password)
257
259 self.username = username
260 self.salt = None
261 self.challenge = None
262 self.response = None
263
265 """
266 I encode a given plaintext password using the salt, and respond
267 to the challenge.
268 """
269 assert self.salt
270 assert self.challenge
271 from Crypto.Hash import SHA256
272 hasher = SHA256.new()
273 hasher.update(self.salt)
274 hasher.update(password)
275 sha256Password = self.salt + dataToHex(hasher.digest())
276 self.response = cryptRespond(self.challenge, sha256Password)
277
279 """
280 Check credentials against the given sha256Password.
281 """
282 if not self.response:
283 return False
284
285 expected = cryptRespond(self.challenge, sha256Password)
286 return self.response == expected
287
289 _algorithm = "MD5"
290
292 self.username = username
293 self.nonce = None
294 self.method = None
295 self.uri = None
296
297 self.qop = None
298 self.cnonce = None
299 self.ncvalue = None
300
301 self.response = None
302
304 expectedResponse = self._calculateRequestDigest(
305 self.username, ha1, self.nonce, self.cnonce,
306 self.method, self.uri, self.ncvalue, self.qop)
307
308 self.debug("Attempting to check calculated response %s against provided response %r", expectedResponse, self.response)
309 self.debug("Username %s, nonce %s, method %s, uri %s, qop %s, cnonce %s, ncvalue %s", self.username, self.nonce, self.method, self.uri, self.qop, self.cnonce, self.ncvalue)
310 self.debug("Using H(A1): %s", ha1)
311
312 if not self.response:
313 return False
314
315 return self.response == expectedResponse
316
318 """
319 Calculate H(A1) as from specification (RFC2617) section 3.2.2, given
320 the initial hash H(username:realm:passwd), hex-encoded.
321
322 This basically applies the second-level hashing for MD5-sess, if
323 required.
324 """
325 if self._algorithm == 'MD5':
326 return ha1
327 elif self._algorithm == 'MD5-sess':
328 HA1 = ha1.decode('hex')
329
330 m = md5.md5()
331 m.update(HA1)
332 m.update(':')
333 m.update(nonce)
334 m.update(':')
335 m.update(cnonce)
336 return m.digest().encode('hex')
337 else:
338 raise NotImplementedError("Unimplemented algorithm")
339
348
369