-- When key not found in section, check key in global's section
function section_mt(section_name)
return { __index = function (t, k)
-- local section = rawget(global_config, section_name);
-- if not section then return nil; end
-- return section[k];
-- end
-- };
++ local section = rawget(global_config, section_name);
++ if not section then return nil; end
++ return section[k];
++ end };
end
function getconfig()
function parsers.lua.load(data, filename)
local env;
-- The ' = true' are needed so as not to set off __newindex when we assign the functions below
-- env = setmetatable({
-- Host = true, host = true, VirtualHost = true,
-- Component = true, component = true,
-- Include = true, include = true, RunScript = dofile }, {
-- __index = function (t, k)
-- return rawget(_G, k) or
-- function (settings_table)
-- config[__currenthost or "*"][k] = settings_table;
-- end;
-- end,
-- __newindex = function (t, k, v)
-- set(env.__currenthost or "*", "core", k, v);
-- end
-- });
++ env = setmetatable({ Host = true, host = true, VirtualHost = true, Component = true, component = true,
++ Include = true, include = true, RunScript = dofile }, { __index = function (t, k)
++ return rawget(_G, k) or
++ function (settings_table)
++ config[__currenthost or "*"][k] = settings_table;
++ end;
++ end,
++ __newindex = function (t, k, v)
++ set(env.__currenthost or "*", "core", k, v);
++ end});
rawset(env, "__currenthost", "*") -- Default is global
function env.VirtualHost(name)
local config = require "core.configmanager";
local hosts = hosts;
--local prosody = _G.prosody;
--
module "usermanager"
--local new_default_provider;
--
--local function host_handler(host)
-- local host_session = hosts[host];
-- host_session.events.add_handler("item-added/auth-provider", function (provider)
-- if config.get(host, "core", "authentication") == provider.name then
-- host_session.users = provider;
-- end
-- end);
-- host_session.events.add_handler("item-removed/auth-provider", function (provider)
-- if host_session.users == provider then
-- host_session.users = new_default_provider(host);
-- end
-- end);
-- host_session.users = new_default_provider(host); -- Start with the default usermanager provider
--end
--prosody.events.add_handler("host-activated", host_handler);
--prosody.events.add_handler("component-activated", host_handler);
--
local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
--function new_default_provider(host)
-- local provider = { name = "default" };
--
-- function provider.test_password(username, password)
-- if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
-- local credentials = datamanager.load(username, host, "accounts") or {};
--
++function validate_credentials(host, username, password, method)
++ log("debug", "User '%s' is being validated", username);
++ if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
++ local credentials = datamanager.load(username, host, "accounts") or {};
++
++ if method == nil then method = "PLAIN"; end
++ if method == "PLAIN" and credentials.password then -- PLAIN, do directly
if password == credentials.password then
return true;
else
return nil, "Auth failed. Invalid username or password.";
end
++ end
++ -- must do md5
++ -- make credentials md5
++ local pwd = credentials.password;
++ if not pwd then pwd = credentials.md5; else pwd = hashes.md5(pwd, true); end
++ -- make password md5
++ if method == "PLAIN" then
++ password = hashes.md5(password or "", true);
++ elseif method ~= "DIGEST-MD5" then
++ return nil, "Unsupported auth method";
end
--
-- function provider.get_password(username)
-- if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
-- return (datamanager.load(username, host, "accounts") or {}).password;
-- end
--
-- function provider.set_password(username, password)
-- if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
-- local account = datamanager.load(username, host, "accounts");
-- if account then
-- account.password = password;
-- return datamanager.store(username, host, "accounts", account);
-- end
-- return nil, "Account not available.";
-- end
--
-- function provider.user_exists(username)
-- if is_cyrus(host) then return true; end
-- return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
-- end
--
-- function provider.create_user(username, password)
-- if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
-- return datamanager.store(username, host, "accounts", {password = password});
-- end
--
-- function provider.get_supported_methods()
-- return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
++ -- compare
++ if password == pwd then
++ return true;
++ else
++ return nil, "Auth failed. Invalid username or password.";
end
--
-- function provider.is_admin(jid)
-- local admins = config.get(host, "core", "admins");
-- if admins ~= config.get("*", "core", "admins") then
-- if type(admins) == "table" then
-- jid = jid_bare(jid);
-- for _,admin in ipairs(admins) do
-- if admin == jid then return true; end
-- end
-- elseif admins then
-- log("error", "Option 'admins' for host '%s' is not a table", host);
-- end
-- end
-- return is_admin(jid); -- Test whether it's a global admin instead
-- end
-- return provider;
--end
--
--function validate_credentials(host, username, password, method)
-- return hosts[host].users.test_password(username, password);
end
function get_password(username, host)
-- return hosts[host].users.get_password(username);
++ if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
++ return (datamanager.load(username, host, "accounts") or {}).password
end
--
function set_password(username, host, password)
-- return hosts[host].users.set_password(username, password);
++ if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
++ local account = datamanager.load(username, host, "accounts");
++ if account then
++ account.password = password;
++ return datamanager.store(username, host, "accounts", account);
++ end
++ return nil, "Account not available.";
end
function user_exists(username, host)
-- return hosts[host].users.user_exists(username);
++ if is_cyrus(host) then return true; end
++ return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
end
function create_user(username, password, host)
-- return hosts[host].users.create_user(username, password);
++ if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
++ return datamanager.store(username, host, "accounts", {password = password});
end
function get_supported_methods(host)
-- return hosts[host].users.get_supported_methods();
++ return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
end
function is_admin(jid, host)
-- if host and host ~= "*" then
-- return hosts[host].users.is_admin(jid);
-- else -- Test only whether this JID is a global admin
-- local admins = config.get("*", "core", "admins");
-- if type(admins) == "table" then
-- jid = jid_bare(jid);
-- for _,admin in ipairs(admins) do
-- if admin == jid then return true; end
-- end
-- elseif admins then
-- log("error", "Option 'admins' for host '%s' is not a table", host);
-- end
++ host = host or "*";
++ local admins = config.get(host, "core", "admins");
++ if host ~= "*" and admins == config.get("*", "core", "admins") then
return nil;
end
++ if type(admins) == "table" then
++ jid = jid_bare(jid);
++ for _,admin in ipairs(admins) do
++ if admin == jid then return true; end
++ end
++ elseif admins then log("warn", "Option 'admins' for host '%s' is not a table", host); end
++ return nil;
end
--_M.new_default_provider = new_default_provider;
--
return _M;
local logger = require "logger";
local log = logger.init("xmppclient_listener");
local lxp = require "lxp"
--local new_xmpp_stream = require "util.xmppstream".new;
++local init_xmlhandlers = require "core.xmlhandlers"
local sm_new_session = require "core.sessionmanager".new_session;
local connlisteners_register = require "net.connlisteners".register;
-- These are session methods --
++local function session_reset_stream(session)
++ -- Reset stream
++ local parser = lxp.new(init_xmlhandlers(session, stream_callbacks), "\1");
++ session.parser = parser;
++
++ session.notopen = true;
++
++ function session.data(conn, data)
++ local ok, err = parser:parse(data);
++ if ok then return; end
++ log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
++ session:close("xml-not-well-formed");
++ end
++
++ return true;
++end
++
local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
local function session_close(session, reason)
conn:setoption("keepalive", opt_keepalives);
end
++ session.reset_stream = session_reset_stream;
session.close = session_close;
-- local stream = new_xmpp_stream(session, stream_callbacks);
-- session.stream = stream;
--
-- session.notopen = true;
--
-- function session.reset_stream()
-- session.notopen = true;
-- session.stream:reset();
-- end
--
-- function session.data(data)
-- local ok, err = stream:feed(data);
-- if ok then return; end
-- log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
-- session:close("xml-not-well-formed");
-- end
++ session_reset_stream(session); -- Initialise, ready for use
session.dispatch_stanza = stream_callbacks.handlestanza;
end
if data then
-- session.data(data);
++ session.data(conn, data);
end
end
end);
-- Don't even think about it!
--if not prosody.start_time then -- server-starting
-- local suid = module:get_option("setuid");
-- if not suid or suid == 0 or suid == "root" then
-- if pposix.getuid() == 0 and not module:get_option("run_as_root") then
-- module:log("error", "Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!");
-- module:log("error", "For more information on running Prosody as root, see http://prosody.im/doc/root");
-- prosody.shutdown("Refusing to run as root");
++module:add_event_hook("server-starting", function ()
++ local suid = module:get_option("setuid");
++ if not suid or suid == 0 or suid == "root" then
++ if pposix.getuid() == 0 and not module:get_option("run_as_root") then
++ module:log("error", "Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!");
++ module:log("error", "For more information on running Prosody as root, see http://prosody.im/doc/root");
++ prosody.shutdown("Refusing to run as root");
++ end
end
-- end
--end
++ end);
local pidfile;
local pidfile_handle;
write_pidfile();
end
end
-- if not prosody.start_time then -- server-starting
-- daemonize_server();
-- end
++ module:add_event_hook("server-starting", daemonize_server);
else
-- Not going to daemonize, so write the pid of this process
write_pidfile();
local history = self._data['history'];
if not history then history = {}; self._data['history'] = history; end
stanza = st.clone(stanza);
-- stanza.attr.to = "";
-- local stamp = datetime.datetime();
-- local chars = #tostring(stanza);
-- stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203
++ stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
-- local entry = { stanza = stanza, stamp = stamp };
-- t_insert(history, entry);
++ t_insert(history, st.preserialize(stanza));
while #history > history_length do t_remove(history, 1) end
end
end
end
end
end
--function room_mt:send_history(to, stanza)
++function room_mt:send_history(to)
local history = self._data['history']; -- send discussion history
if history then
-- local x_tag = stanza and stanza:get_child("x", "http://jabber.org/protocol/muc");
-- local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc");
--
-- local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
-- if maxchars then maxchars = math.floor(maxchars); end
--
-- local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
-- if not history_tag then maxstanzas = 20; end
--
-- local seconds = history_tag and tonumber(history_tag.attr.seconds);
-- if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end
--
-- local since = history_tag and history_tag.attr.since;
-- if since and not since:match("^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d:%d%dZ$") then since = nil; end -- FIXME timezone support
-- if seconds and (not since or since < seconds) then since = seconds; end
--
-- local n = 0;
-- local charcount = 0;
-- local stanzacount = 0;
--
-- for i=#history,1,-1 do
-- local entry = history[i];
-- if maxchars then
-- if not entry.chars then
-- entry.stanza.attr.to = "";
-- entry.chars = #tostring(entry.stanza);
-- end
-- charcount = charcount + entry.chars + #to;
-- if charcount > maxchars then break; end
-- end
-- if since and since > entry.stamp then break; end
-- if n + 1 > maxstanzas then break; end
-- n = n + 1;
-- end
-- for i=#history-n+1,#history do
-- local msg = history[i].stanza;
-- msg.attr.to = to;
++ for _, msg in ipairs(history) do
++ msg = st.deserialize(msg);
++ msg.attr.to=to;
self:_route_stanza(msg);
end
end
:tag("item", {affiliation=affiliation or "none", role=role or "none"}):up()
:tag("status", {code='110'}));
end
-- self:send_history(from, stanza);
++ if self._data.whois == 'anyone' then -- non-anonymous?
++ self:_route_stanza(st.stanza("message", {from=to, to=from, type='groupchat'})
++ :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
++ :tag("status", {code='100'}));
++ end
++ self:send_history(from);
else -- banned
local reply = st.error_reply(stanza, "auth", "forbidden"):up();
reply.tags[1].attr.code = "403";
function room_mt:set_role(actor, occupant_jid, role, callback, reason)
if role == "none" then role = nil; end
if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end
-- if self:get_affiliation(actor) ~= "owner" then return nil, "cancel", "not-allowed"; end
++ if self:get_role(self._jid_nick[actor]) ~= "moderator" then return nil, "cancel", "not-allowed"; end
local occupant = self._occupants[occupant_jid];
if not occupant then return nil, "modify", "not-acceptable"; end
if occupant.affiliation == "owner" or occupant.affiliation == "admin" then return nil, "cancel", "not-allowed"; end
end
end
end
-- if self._data.whois == 'anyone' then
-- muc_child:tag('status', { code = '100' });
-- end
end
self:route_stanza(stanza);
if muc_child then
package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
end
++package.path = package.path..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.lua";
++package.cpath = package.cpath..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.so";
++
-- Substitute ~ with path to home directory in data path
if CFG_DATADIR then
if os.getenv("HOME") then
end
end
---- Global 'prosody' object
--prosody = { events = require "util.events".new(); };
--local prosody = prosody;
--
-- Load the config-parsing module
config = require "core.configmanager"
full_sessions = {};
hosts = {};
++ -- Global 'prosody' object
++ prosody = {};
++ local prosody = prosody;
++
prosody.bare_sessions = bare_sessions;
prosody.full_sessions = full_sessions;
prosody.hosts = hosts;
prosody.arg = _G.arg;
++ prosody.events = require "util.events".new();
++
prosody.platform = "unknown";
if os.getenv("WINDIR") then
prosody.platform = "windows";
-- Function to reopen logfiles
function prosody.reopen_logfiles()
log("info", "Re-opening log files");
++ eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
prosody.events.fire_event("reopen-log-files");
end
function load_secondary_libraries()
--- Load and initialise core modules
require "util.import"
-- require "util.xmppstream"
require "core.xmlhandlers"
require "core.rostermanager"
++ require "core.eventmanager"
require "core.hostmanager"
require "core.modulemanager"
require "core.usermanager"
function prepare_to_start()
log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
-- Signal to modules that we are ready to start
++ eventmanager.fire_event("server-starting");
prosody.events.fire_event("server-starting");
-- start listening on sockets
init_global_protection();
prepare_to_start();
++eventmanager.fire_event("server-started");
prosody.events.fire_event("server-started");
loop();
log("info", "Shutting down...");
cleanup();
++eventmanager.fire_event("server-stopped");
prosody.events.fire_event("server-stopped");
log("info", "Shutdown complete");
end
end
---- Global 'prosody' object
--prosody = {
-- hosts = {},
-- events = require "util.events".new(),
-- platform = "posix"
--};
--local prosody = prosody;
--
config = require "core.configmanager"
do
os.exit(1);
end
++prosody = { hosts = {}, events = events, platform = "posix" };
++
local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
require "util.datamanager".set_data_path(data_path);
["not-running"] = "Prosody is not running";
}, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end });
--hosts = prosody.hosts;
++local events = require "util.events".new();
--local function make_host(hostname)
-- return { events = prosody.events, users = require "core.usermanager".new_default_provider(hostname) };
--end
++hosts = prosody.hosts;
for hostname, config in pairs(config.getconfig()) do
-- hosts[hostname] = make_host(hostname);
++ hosts[hostname] = { events = events };
end
require "core.modulemanager"
return 1;
end
-- if not hosts[host] then
-- show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
-- show_warning("The user will not be able to log in until this is changed.");
-- hosts[host] = make_host(host);
-- elseif config.get(host, "core", "authentication")
-- and config.get(host, "core", "authentication") ~= "default" then
-- show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
-- config.get(host, "core", "authentication"));
-- show_warning("prosodyctl currently only supports the default provider, sorry :(");
-- return 1;
-- end
--
if prosodyctl.user_exists{ user = user, host = host } then
show_message [[That user already exists]];
return 1;
end
++ if not hosts[host] then
++ show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
++ show_warning("The user will not be able to log in until this is changed.");
++ end
++
local password = read_password();
if not password then return 1; end
return 1;
end
-- if not hosts[host] then
-- show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
-- show_warning("The user will not be able to log in until this is changed.");
-- hosts[host] = make_host(host);
-- elseif config.get(host, "core", "authentication")
-- and config.get(host, "core", "authentication") ~= "default" then
-- show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
-- config.get(host, "core", "authentication"));
-- show_warning("prosodyctl currently only supports the default provider, sorry :(");
-- return 1;
-- end
--
if not prosodyctl.user_exists { user = user, host = host } then
show_message [[That user does not exist, use prosodyctl adduser to create a new user]]
return 1;
return 1;
end
-- if not hosts[host] then
-- show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
-- show_warning("The user will not be able to log in until this is changed.");
-- hosts[host] = make_host(host);
-- elseif config.get(host, "core", "authentication")
-- and config.get(host, "core", "authentication") ~= "default" then
-- show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
-- config.get(host, "core", "authentication"));
-- show_warning("prosodyctl currently only supports the default provider, sorry :(");
-- return 1;
-- end
--
if not prosodyctl.user_exists { user = user, host = host } then
show_message [[That user does not exist on this server]]
return 1;
state = false : disabled
state = true : enabled
state = nil : non-existant
++
++plain:
++ function(username, realm)
++ return password, state;
++ end
++
++plain-test:
++ function(username, realm, password)
++ return true or false, state;
++ end
++
++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 method = {};
-- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
--
-- All rights reserved.
--
--=========================
--SASL ANONYMOUS according to RFC 4505
--
----[[
--Supported Authentication Backends
--
--anonymous:
-- function(username, realm)
-- return true; --for normal usage just return true; if you don't like the supplied username you can return false.
-- end
--]]
--
local function anonymous(self, message)
local username;
repeat
-- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
--
-- All rights reserved.
--
--=========================
--SASL DIGEST-MD5 according to RFC 2831
----[[
--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
-- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
--
-- All rights reserved.
--
-- ================================
-- SASL PLAIN according to RFC 4616
--
----[[
--Supported Authentication Backends
--
--plain:
-- function(username, realm)
-- return password, state;
-- end
--
--plain-test:
-- function(username, realm, password)
-- return true or false, state;
-- end
--
--plain-hashed:
-- function(username, realm)
-- return hashed_password, hash_function, state;
-- end
--]]
--
local function plain(self, message)
if not message then
return "failure", "malformed-request";
if correct_password == password then correct = true; else correct = false; end
elseif self.profile.plain_test then
correct, state = self.profile.plain_test(authentication, self.realm, password);
-- elseif self.profile.plain_hashed then
-- local hashed_password, hash_f;
-- hashed_password, hash_f, state = self.profile.plain_hashed(authentication, self.realm);
-- if hashed_password == hash_f(password) then correct = true; else correct = false; end
end
self.username = authentication
end
function init(registerMechanism)
-- registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain);
++ registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
end
return _M;
-- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
--
-- All rights reserved.
--
--=========================
--SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10
--
----[[
--Supported Authentication Backends
--
--scram-{MECH}:
-- function(username, realm)
-- return salted_password, iteration_count, salt, state;
-- end
--]]
--
local default_i = 4096
local function bp( b )
return username;
end
--local function scram_gen(hash_name, H_f, HMAC_f)
-- local function scram_hash(self, message)
-- if not self.state then self["state"] = {} end
++local function scram_sha_1(self, message)
++ if not self.state then self["state"] = {} end
-- if not self.state.name then
-- -- we are processing client_first_message
-- local client_first_message = message;
-- self.state["client_first_message"] = client_first_message;
-- self.state["name"] = client_first_message:match("n=(.+),r=")
-- self.state["clientnonce"] = client_first_message:match("r=([^,]+)")
++ if not self.state.name then
++ -- we are processing client_first_message
++ local client_first_message = message;
++ self.state["client_first_message"] = client_first_message;
++ self.state["name"] = client_first_message:match("n=(.+),r=")
++ self.state["clientnonce"] = client_first_message:match("r=([^,]+)")
-- if not self.state.name or not self.state.clientnonce then
-- return "failure", "malformed-request";
-- end
++ if not self.state.name or not self.state.clientnonce then
++ return "failure", "malformed-request";
++ end
-- self.state.name = validate_username(self.state.name);
-- if not self.state.name then
-- log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
-- return "failure", "malformed-request", "Invalid username.";
-- end
++ self.state.name = validate_username(self.state.name);
++ if not self.state.name then
++ log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
++ return "failure", "malformed-request", "Invalid username.";
++ end
-- self.state["servernonce"] = generate_uuid();
--
-- -- retreive credentials
-- if self.profile.plain then
-- local password, state = self.profile.plain(self.state.name, self.realm)
-- if state == nil then return "failure", "not-authorized"
-- elseif state == false then return "failure", "account-disabled" end
--
-- password = saslprep(password);
-- if not password then
-- log("debug", "Password violates SASLprep.");
-- return "failure", "not-authorized", "Invalid password."
-- end
-- self.state.salt = generate_uuid();
-- self.state.iteration_count = default_i;
-- self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i);
-- elseif self.profile["scram_"..hash_name] then
-- local salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm);
-- if state == nil then return "failure", "not-authorized"
-- elseif state == false then return "failure", "account-disabled" end
--
-- self.state.salted_password = salted_password;
-- self.state.iteration_count = iteration_count;
-- self.state.salt = salt
-- end
++ self.state["servernonce"] = generate_uuid();
++ self.state["salt"] = generate_uuid();
-- local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..self.state.iteration_count;
-- self.state["server_first_message"] = server_first_message;
-- return "challenge", server_first_message
-- else
-- if type(message) ~= "string" then return "failure", "malformed-request" end
-- -- we are processing client_final_message
-- local client_final_message = message;
++ local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i;
++ self.state["server_first_message"] = server_first_message;
++ return "challenge", server_first_message
++ else
++ if type(message) ~= "string" then return "failure", "malformed-request" end
++ -- we are processing client_final_message
++ local client_final_message = message;
-- self.state["proof"] = client_final_message:match("p=(.+)");
-- self.state["nonce"] = client_final_message:match("r=(.+),p=");
-- self.state["channelbinding"] = client_final_message:match("c=(.+),r=");
-- if not self.state.proof or not self.state.nonce or not self.state.channelbinding then
-- return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
++ self.state["proof"] = client_final_message:match("p=(.+)");
++ self.state["nonce"] = client_final_message:match("r=(.+),p=");
++ self.state["channelbinding"] = client_final_message:match("c=(.+),r=");
++ if not self.state.proof or not self.state.nonce or not self.state.channelbinding then
++ return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
++ end
++
++ local password, state;
++ if self.profile.plain then
++ password, state = self.profile.plain(self.state.name, self.realm)
++ if state == nil then return "failure", "not-authorized"
++ elseif state == false then return "failure", "account-disabled" end
++ password = saslprep(password);
++ if not password then
++ log("debug", "Password violates SASLprep.");
++ return "failure", "not-authorized", "Invalid password."
end
++ end
-- local SaltedPassword = self.state.salted_password;
-- local ClientKey = HMAC_f(SaltedPassword, "Client Key")
-- local ServerKey = HMAC_f(SaltedPassword, "Server Key")
-- local StoredKey = H_f(ClientKey)
-- local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
-- local ClientSignature = HMAC_f(StoredKey, AuthMessage)
-- local ClientProof = binaryXOR(ClientKey, ClientSignature)
-- local ServerSignature = HMAC_f(ServerKey, AuthMessage)
++ local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i)
++ local ClientKey = hmac_sha1(SaltedPassword, "Client Key")
++ local ServerKey = hmac_sha1(SaltedPassword, "Server Key")
++ local StoredKey = sha1(ClientKey)
++ local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
++ local ClientSignature = hmac_sha1(StoredKey, AuthMessage)
++ local ClientProof = binaryXOR(ClientKey, ClientSignature)
++ local ServerSignature = hmac_sha1(ServerKey, AuthMessage)
-- if base64.encode(ClientProof) == self.state.proof then
-- local server_final_message = "v="..base64.encode(ServerSignature);
-- self["username"] = self.state.name;
-- return "success", server_final_message;
-- else
-- return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
-- end
++ if base64.encode(ClientProof) == self.state.proof then
++ local server_final_message = "v="..base64.encode(ServerSignature);
++ self["username"] = self.state.name;
++ return "success", server_final_message;
++ else
++ return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
end
end
-- return scram_hash;
end
function init(registerMechanism)
-- local function registerSCRAMMechanism(hash_name, hash, hmac_hash)
-- registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash));
-- end
--
-- registerSCRAMMechanism("SHA-1", sha1, hmac_sha1);
++ registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1);
end
return _M;