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