Software /
code /
prosody
Comparison
util/sasl.lua @ 2184:0d1740f7b6e8 sasl
Store stage in SASL object.
author | Tobias Markmann <tm@ayena.de> |
---|---|
date | Fri, 28 Aug 2009 22:01:58 +0200 |
parent | 2183:44e71e65da86 |
child | 2185:e92339c48ee6 |
comparison
equal
deleted
inserted
replaced
2183:44e71e65da86 | 2184:0d1740f7b6e8 |
---|---|
149 end | 149 end |
150 registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); | 150 registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); |
151 | 151 |
152 --========================= | 152 --========================= |
153 --SASL DIGEST-MD5 according to RFC 2831 | 153 --SASL DIGEST-MD5 according to RFC 2831 |
154 local function new_digest_md5(realm, credentials_handler) | 154 local function sasl_mechanism_digest_md5(self, message) |
155 --TODO complete support for authzid | 155 --TODO complete support for authzid |
156 | 156 |
157 local function serialize(message) | 157 local function serialize(message) |
158 local data = "" | 158 local data = "" |
159 | 159 |
223 message[k] = v; | 223 message[k] = v; |
224 end | 224 end |
225 return message; | 225 return message; |
226 end | 226 end |
227 | 227 |
228 local object = { mechanism = "DIGEST-MD5", realm = realm, credentials_handler = credentials_handler}; | 228 if not self.nonce then |
229 | 229 self.nonce = generate_uuid(); |
230 object.nonce = generate_uuid(); | 230 self.step = 0; |
231 object.step = 0; | 231 self.nonce_count = {}; |
232 object.nonce_count = {}; | 232 end |
233 | 233 |
234 function object.feed(self, message) | 234 self.step = self.step + 1; |
235 self.step = self.step + 1; | 235 if (self.step == 1) then |
236 if (self.step == 1) then | 236 local challenge = serialize({ nonce = object.nonce, |
237 local challenge = serialize({ nonce = object.nonce, | 237 qop = "auth", |
238 qop = "auth", | 238 charset = "utf-8", |
239 charset = "utf-8", | 239 algorithm = "md5-sess", |
240 algorithm = "md5-sess", | 240 realm = self.realm}); |
241 realm = self.realm}); | 241 return "challenge", challenge; |
242 return "challenge", challenge; | 242 elseif (self.step == 2) then |
243 elseif (self.step == 2) then | 243 local response = parse(message); |
244 local response = parse(message); | 244 -- check for replay attack |
245 -- check for replay attack | 245 if response["nc"] then |
246 if response["nc"] then | 246 if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end |
247 if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end | 247 end |
248 end | 248 |
249 | 249 -- check for username, it's REQUIRED by RFC 2831 |
250 -- check for username, it's REQUIRED by RFC 2831 | 250 if not response["username"] then |
251 if not response["username"] then | 251 return "failure", "malformed-request"; |
252 return "failure", "malformed-request"; | 252 end |
253 end | 253 self["username"] = response["username"]; |
254 self["username"] = response["username"]; | 254 |
255 | 255 -- check for nonce, ... |
256 -- check for nonce, ... | 256 if not response["nonce"] then |
257 if not response["nonce"] then | 257 return "failure", "malformed-request"; |
258 return "failure", "malformed-request"; | 258 else |
259 -- check if it's the right nonce | |
260 if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end | |
261 end | |
262 | |
263 if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end | |
264 if not response["qop"] then response["qop"] = "auth" end | |
265 | |
266 if response["realm"] == nil or response["realm"] == "" then | |
267 response["realm"] = ""; | |
268 elseif response["realm"] ~= self.realm then | |
269 return "failure", "not-authorized", "Incorrect realm value"; | |
270 end | |
271 | |
272 local decoder; | |
273 if response["charset"] == nil then | |
274 decoder = utf8tolatin1ifpossible; | |
275 elseif response["charset"] ~= "utf-8" then | |
276 return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; | |
277 end | |
278 | |
279 local domain = ""; | |
280 local protocol = ""; | |
281 if response["digest-uri"] then | |
282 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); | |
283 if protocol == nil or domain == nil then return "failure", "malformed-request" end | |
284 else | |
285 return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." | |
286 end | |
287 | |
288 --TODO maybe realm support | |
289 self.username = response["username"]; | |
290 local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); | |
291 if Y == nil then return "failure", "not-authorized" | |
292 elseif Y == false then return "failure", "account-disabled" end | |
293 local A1 = ""; | |
294 if response.authzid then | |
295 if response.authzid == self.username.."@"..self.realm then | |
296 -- COMPAT | |
297 log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); | |
298 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; | |
259 else | 299 else |
260 -- check if it's the right nonce | 300 A1 = "?"; |
261 if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end | 301 end |
262 end | 302 else |
263 | 303 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; |
264 if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end | 304 end |
265 if not response["qop"] then response["qop"] = "auth" end | 305 local A2 = "AUTHENTICATE:"..protocol.."/"..domain; |
266 | 306 |
267 if response["realm"] == nil or response["realm"] == "" then | 307 local HA1 = md5(A1, true); |
268 response["realm"] = ""; | 308 local HA2 = md5(A2, true); |
269 elseif response["realm"] ~= self.realm then | 309 |
270 return "failure", "not-authorized", "Incorrect realm value"; | 310 local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; |
271 end | 311 local response_value = md5(KD, true); |
272 | 312 |
273 local decoder; | 313 if response_value == response["response"] then |
274 if response["charset"] == nil then | 314 -- calculate rspauth |
275 decoder = utf8tolatin1ifpossible; | 315 A2 = ":"..protocol.."/"..domain; |
276 elseif response["charset"] ~= "utf-8" then | 316 |
277 return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; | 317 HA1 = md5(A1, true); |
278 end | 318 HA2 = md5(A2, true); |
279 | 319 |
280 local domain = ""; | 320 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 |
281 local protocol = ""; | 321 local rspauth = md5(KD, true); |
282 if response["digest-uri"] then | 322 self.authenticated = true; |
283 protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); | 323 return "challenge", serialize({rspauth = rspauth}); |
284 if protocol == nil or domain == nil then return "failure", "malformed-request" end | 324 else |
285 else | 325 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." |
286 return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." | 326 end |
287 end | 327 elseif self.step == 3 then |
288 | 328 if self.authenticated ~= nil then return "success" |
289 --TODO maybe realm support | 329 else return "failure", "malformed-request" end |
290 self.username = response["username"]; | 330 end |
291 local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); | |
292 if Y == nil then return "failure", "not-authorized" | |
293 elseif Y == false then return "failure", "account-disabled" end | |
294 local A1 = ""; | |
295 if response.authzid then | |
296 if response.authzid == self.username.."@"..self.realm then | |
297 -- COMPAT | |
298 log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); | |
299 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; | |
300 else | |
301 A1 = "?"; | |
302 end | |
303 else | |
304 A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; | |
305 end | |
306 local A2 = "AUTHENTICATE:"..protocol.."/"..domain; | |
307 | |
308 local HA1 = md5(A1, true); | |
309 local HA2 = md5(A2, true); | |
310 | |
311 local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; | |
312 local response_value = md5(KD, true); | |
313 | |
314 if response_value == response["response"] then | |
315 -- calculate rspauth | |
316 A2 = ":"..protocol.."/"..domain; | |
317 | |
318 HA1 = md5(A1, true); | |
319 HA2 = md5(A2, true); | |
320 | |
321 KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 | |
322 local rspauth = md5(KD, true); | |
323 self.authenticated = true; | |
324 return "challenge", serialize({rspauth = rspauth}); | |
325 else | |
326 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." | |
327 end | |
328 elseif self.step == 3 then | |
329 if self.authenticated ~= nil then return "success" | |
330 else return "failure", "malformed-request" end | |
331 end | |
332 end | |
333 return object; | |
334 end | 331 end |
335 | 332 |
336 return _M; | 333 return _M; |