-- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
--
-- All rights reserved.
--
local md5 = require "util.hashes".md5;
local log = require "util.logger".init("sasl");
local generate_uuid = require "util.uuid".generate;
+local nodeprep = require "util.encodings".stringprep.nodeprep;
-module "plain"
+local _ENV = nil;
--=========================
--SASL DIGEST-MD5 according to RFC 2831
-local function digest_response()
-
- return response, A1, A2
-end
+
+--[[
+Supported Authentication Backends
+
+digest_md5:
+ function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken
+ -- implementations it's not
+ return digesthash, state;
+ end
+
+digest_md5_test:
+ function(username, domain, realm, encoding, digesthash)
+ return true or false, state;
+ end
+]]
local function digest(self, message)
--TODO complete support for authzid
local function serialize(message)
local data = ""
- if type(message) ~= "table" then error("serialize needs an argument of type table.") end
-
-- testing all possible values
if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end
if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end
end
local function parse(data)
local message = {}
- for k, v in s_gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder
+ -- COMPAT: %z in the pattern to work around jwchat bug (sends "charset=utf-8\0")
+ for k, v in s_gmatch(data, [[([%w%-]+)="?([^",%z]*)"?,?]]) do -- FIXME The hacky regex makes me shudder
message[k] = v;
end
return message;
end
-- check for username, it's REQUIRED by RFC 2831
- if not response["username"] then
+ local username = response["username"];
+ local _nodeprep = self.profile.nodeprep;
+ if username and _nodeprep ~= false then
+ username = (_nodeprep or nodeprep)(username); -- FIXME charset
+ end
+ if not username or username == "" then
return "failure", "malformed-request";
end
- self["username"] = response["username"];
+ self.username = username;
-- check for nonce, ...
if not response["nonce"] then
end
--TODO maybe realm support
- self.username = response["username"];
+ local Y, state;
if self.profile.plain then
- local password, state = self.profile.plain(response["username"], self.realm)
+ local password, state = self.profile.plain(self, response["username"], self.realm)
if state == nil then return "failure", "not-authorized"
elseif state == false then return "failure", "account-disabled" end
Y = md5(response["username"]..":"..response["realm"]..":"..password);
elseif self.profile["digest-md5"] then
- local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"] response["charset"])
+ Y, state = self.profile["digest-md5"](self, response["username"], self.realm, response["realm"], response["charset"])
if state == nil then return "failure", "not-authorized"
elseif state == false then return "failure", "account-disabled" end
elseif self.profile["digest-md5-test"] then
--elseif Y == false then return "failure", "account-disabled" end
local A1 = "";
if response.authzid then
- if response.authzid == self.username.."@"..self.realm then
+ if response.authzid == self.username or response.authzid == self.username.."@"..self.realm then
-- COMPAT
- log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920.");
+ log("warn", "Client is violating RFC 3920 (section 6.1, point 7).");
A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid;
else
- A1 = "?";
+ return "failure", "invalid-authzid";
end
else
A1 = Y..":"..response["nonce"]..":"..response["cnonce"];
end
end
-function init(registerMechanism)
+local function init(registerMechanism)
registerMechanism("DIGEST-MD5", {"plain"}, digest);
end
-return _M;
\ No newline at end of file
+return {
+ init = init;
+}