Merge backout
authorMatthew Wild <mwild1@gmail.com>
Tue, 29 Jun 2010 19:23:11 +0000 (20:23 +0100)
committerMatthew Wild <mwild1@gmail.com>
Tue, 29 Jun 2010 19:23:11 +0000 (20:23 +0100)
14 files changed:
1  2 
core/rostermanager.lua
core/s2smanager.lua
core/sessionmanager.lua
core/usermanager.lua
plugins/mod_announce.lua
plugins/mod_groups.lua
plugins/mod_posix.lua
plugins/mod_presence.lua
plugins/mod_saslauth.lua
prosody
prosodyctl
util/sasl/anonymous.lua
util/sasl/plain.lua
util/sasl/scram.lua

index 59ba6579a24af27fe78470de5473bb4ff1347698,e2a92696391063c72c55026e47db68d1871602e5..506cf20593aa7da447d09ca5836517bbda1bab3b
@@@ -190,22 -186,10 +190,10 @@@ function process_inbound_unsubscribe(us
        end
  end
  
- local function _get_online_roster_subscription(jidA, jidB)
-       local user = bare_sessions[jidA];
-       local item = user and (user.roster[jidB] or { subscription = "none" });
-       return item and item.subscription;
- end
  function is_contact_subscribed(username, host, jid)
-       do
-               local selfjid = username.."@"..host;
-               local subscription = _get_online_roster_subscription(selfjid, jid);
-               if subscription then return (subscription == "both" or subscription == "from"); end
-               local subscription = _get_online_roster_subscription(jid, selfjid);
-               if subscription then return (subscription == "both" or subscription == "to"); end
-       end
 -      local roster = load_roster(username, host);
 +      local roster, err = load_roster(username, host);
        local item = roster[jid];
 -      return item and (item.subscription == "from" or item.subscription == "both");
 +      return item and (item.subscription == "from" or item.subscription == "both"), err;
  end
  
  function is_contact_pending_in(username, host, jid)
Simple merge
index 6376851533021b51b0a10b3f2730a8a41c3039a6,6e771a8444819c261e114f7dbd0d8461b1efbf24..e1f1a80214e9540abcea1a17c023a8d49e0889f1
@@@ -25,9 -25,7 +25,8 @@@ local rm_load_roster = require "core.ro
  local config_get = require "core.configmanager".get;
  local nameprep = require "util.encodings".stringprep.nameprep;
  local resourceprep = require "util.encodings".stringprep.resourceprep;
 +local nodeprep = require "util.encodings".stringprep.nodeprep;
  
- local initialize_filters = require "util.filters".initialize;
  local fire_event = require "core.eventmanager".fire_event;
  local add_task = require "util.timer".add_task;
  local gettime = require "socket".gettime;
index dd17128c17d7dfa22f69946cbc8098988c3f0485,1ee4736e3791bbba1bf56b9be8a03f0d6404e270..698d2f10413676bc28f3779e94b0f5242fe73e78
@@@ -17,117 -16,117 +16,85 @@@ local jid_bare = require "util.jid".bar
  local config = require "core.configmanager";
  local hosts = hosts;
  
 -local prosody = _G.prosody;
 +local require_provisioning = config.get("*", "core", "cyrus_require_provisioning") or false;
  
- local prosody = _G.prosody;
- local setmetatable = setmetatable;
- local default_provider = "internal";
  module "usermanager"
  
- function new_null_provider()
-       local function dummy() end;
-       return setmetatable({name = "null"}, { __index = function() return dummy; end });
- end
 -local new_default_provider;
 -
 -prosody.events.add_handler("host-activated", function (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);
 -
+ local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
  
- function initialize_host(host)
-       local host_session = hosts[host];
-       host_session.events.add_handler("item-added/auth-provider", function (event)
-               local provider = event.item;
-               local auth_provider = config.get(host, "core", "authentication") or default_provider;
-               if provider.name == auth_provider then
-                       host_session.users = provider;
-               end
-               if host_session.users ~= nil and host_session.users.name ~= nil then
-                       log("debug", "host '%s' now set to use user provider '%s'", host, host_session.users.name);
-               end
-       end);
-       host_session.events.add_handler("item-removed/auth-provider", function (event)
-               local provider = event.item;
-               if host_session.users == provider then
-                       host_session.users = new_null_provider();
-               end
-       end);
-       host_session.users = new_null_provider(); -- Start with the default usermanager provider
-       local auth_provider = config.get(host, "core", "authentication") or default_provider;
-       if auth_provider ~= "null" then
-               modulemanager.load(host, "auth_"..auth_provider);
-       end
- end;
- prosody.events.add_handler("host-activated", initialize_host, 100);
- prosody.events.add_handler("component-activated", initialize_host, 100);
 -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 {};
 +
- function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
- function test_password(username, password, host)
-       return hosts[host].users.test_password(username, password);
++      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)
 -              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
 -      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, password, host)
-       return hosts[host].users.set_password(username, password);
+ 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 not(require_provisioning) and is_cyrus(host) then return true; end
++      local account, err = datamanager.load(username, host, "accounts");
++      return (account or err) ~= nil; -- FIXME also check for empty credentials
  end
  
  function create_user(username, password, host)
--      return hosts[host].users.create_user(username, password);
++      if not(require_provisioning) and 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_sasl_handler(host)
-       return hosts[host].users.get_sasl_handler();
- end
- function get_provider(host)
-       return hosts[host].users;
+ 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)
-       local is_admin;
-       jid = jid_bare(jid);
 -      return hosts[host].users.is_admin(jid);
 +      host = host or "*";
-       
-       local host_admins = config.get(host, "core", "admins");
-       local global_admins = config.get("*", "core", "admins");
-       
-       if host_admins and host_admins ~= global_admins then
-               if type(host_admins) == "table" then
-                       for _,admin in ipairs(host_admins) do
-                               if admin == jid then
-                                       is_admin = true;
-                                       break;
-                               end
-                       end
-               elseif admins then
-                       log("error", "Option 'admins' for host '%s' is not a list", host);
-               end
++      local admins = config.get(host, "core", "admins");
++      if host ~= "*" and admins == config.get("*", "core", "admins") then
++              return nil;
 +      end
-       
-       if not is_admin and global_admins then
-               if type(global_admins) == "table" then
-                       for _,admin in ipairs(global_admins) do
-                               if admin == jid then
-                                       is_admin = true;
-                                       break;
-                               end
-                       end
-               elseif admins then
-                       log("error", "Global option 'admins' is not a list");
++      if type(admins) == "table" then
++              jid = jid_bare(jid);
++              for _,admin in ipairs(admins) do
++                      if admin == jid then return true; end
 +              end
-       end
-       
-       -- Still not an admin, check with auth provider
-       if not is_admin and host ~= "*" and hosts[host].users.is_admin then
-               is_admin = hosts[host].users.is_admin(jid);
-       end
-       return is_admin or false;
++      elseif admins then log("warn", "Option 'admins' for host '%s' is not a table", host); end
++      return nil;
  end
  
  return _M;
index 77555bec73b6aec191bc8dd1a8d594b66948aa06,7f08a6e0ecad85980e7b87a2c3fa8f6dbde96ade..d3017f6c994f0cc33ac5f495f5b8b333cf2d9d40
@@@ -45,7 -21,8 +21,7 @@@ function handle_announcement(data
        
        if not is_admin(stanza.attr.from) then
                -- Not an admin? Not allowed!
-               module:log("warn", "Non-admin '%s' tried to send server announcement", stanza.attr.from);
+               module:log("warn", "Non-admin %s tried to send server announcement", tostring(jid.bare(stanza.attr.from)));
 -              origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
                return;
        end
        
Simple merge
index 52b1e0e6e510533ca87987ce01da71b0a1d02177,5e9d25442aebb4012ea9b20a180546382185ed0f..c38f7eba75399bb9d9e421595f278454c77ec573
@@@ -54,16 -54,16 +54,16 @@@ module:add_event_hook("server-started"
        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;
@@@ -141,9 -140,9 +141,7 @@@ if daemonize the
                        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();
index 8e6ef85a35ab97ab928f6a7e7a4dcbd6d9c5c492,5ad3bfdf28d07c6f108ef7b9939c320e15ca5590..4fb8c3e4ec8323cebab4ac0a5e1e2cb893a88bc3
@@@ -24,17 -24,28 +24,31 @@@ local rostermanager = require "core.ros
  local sessionmanager = require "core.sessionmanager";
  local offlinemanager = require "core.offlinemanager";
  
 -local select_top_resources;
 -local bare_message_delivery_policy = module:get_option("bare_message_delivery_policy") or "priority";
 -if bare_message_delivery_policy == "broadcast" then
 -      function select_top_resources(user)
 -              local recipients = {};
 -              for _, session in pairs(user.sessions) do -- find resources with non-negative priority
+ local _core_route_stanza = core_route_stanza;
+ local core_route_stanza;
+ function core_route_stanza(origin, stanza)
+       if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" and stanza.attr.type ~= "error" then
+               local node, host = jid_split(stanza.attr.to);
+               host = hosts[host];
+               if node and host and host.type == "local" then
+                       handle_inbound_presence_subscriptions_and_probes(origin, stanza, jid_bare(stanza.attr.from), jid_bare(stanza.attr.to), core_route_stanza);
+                       return;
+               end
+       end
+       _core_route_stanza(origin, stanza);
+ end
 +local function select_top_resources(user)
 +      local priority = 0;
 +      local recipients = {};
 +      for _, session in pairs(user.sessions) do -- find resource with greatest priority
 +              if session.presence then
 +                      -- TODO check active privacy list for session
                        local p = session.priority;
 -                      if p and p >= 0 then
 +                      if p > priority then
 +                              priority = p;
 +                              recipients = {session};
 +                      elseif p == priority then
                                t_insert(recipients, session);
                        end
                end
@@@ -48,17 -81,7 +62,17 @@@ local function recalc_resource_map(user
        end
  end
  
- function handle_normal_presence(origin, stanza)
 +local ignore_presence_priority = module:get_option("ignore_presence_priority");
 +
+ function handle_normal_presence(origin, stanza, core_route_stanza)
 +      if ignore_presence_priority then
 +              local priority = stanza:child_with_name("priority");
 +              if priority and priority[1] ~= "0" then
 +                      for i=#priority.tags,1,-1 do priority.tags[i] = nil; end
 +                      for i=#priority,1,-1 do priority[i] = nil; end
 +                      priority[1] = "0";
 +              end
 +      end
        if full_sessions[origin.full_jid] then -- if user is still connected
                origin.send(stanza); -- reflect their presence back to them
        end
@@@ -217,14 -236,17 +227,14 @@@ function handle_inbound_presence_subscr
        stanza.attr.from, stanza.attr.to = from_bare, to_bare;
        log("debug", "inbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
        
 -      if not node then
 -              log("debug", "dropping presence sent to host or invalid address '%s'", tostring(to_bare));
 -      end
 -      
        if stanza.attr.type == "probe" then
 -              if rostermanager.is_contact_subscribed(node, host, from_bare) then
 +              local result, err = rostermanager.is_contact_subscribed(node, host, from_bare);
 +              if result then
-                       if 0 == send_presence_of_available_resources(node, host, st_from, origin) then
-                               core_post_stanza(hosts[host], st.presence({from=to_bare, to=st_from, type="unavailable"}), true); -- TODO send last activity
+                       if 0 == send_presence_of_available_resources(node, host, st_from, origin, core_route_stanza) then
 -                              core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity
++                              core_route_stanza(hosts[host], st.presence({from=to_bare, to=st_from, type="unavailable"})); -- TODO send last activity
                        end
 -              else
 +              elseif not err then
-                       core_post_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"}), true);
+                       core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"}));
                end
        elseif stanza.attr.type == "subscribe" then
                if rostermanager.is_contact_subscribed(node, host, from_bare) then
Simple merge
diff --cc prosody
Simple merge
diff --cc prosodyctl
index 4aaf90c5a1a8f3ff2ff8665bc4c0ce295e190cc9,8b2485ad59114bf54ce17d9db24f90833dff49c5..26183b21204af1db3b7ea35f26ea8256e2157645
@@@ -29,14 -29,14 +29,6 @@@ if CFG_DATADIR the
        end
  end
  
---- Global 'prosody' object
--prosody = {
--      hosts = {},
--      events = require "util.events".new(),
--      platform = "posix"
--};
--local prosody = prosody;
--
  config = require "core.configmanager"
  
  do
@@@ -71,6 -71,6 +63,8 @@@ if not require "util.dependencies".chec
        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);
  
@@@ -120,14 -120,10 +114,12 @@@ local error_messages = setmetatable(
                ["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_null_provider(hostname) };
- end
+ hosts = prosody.hosts;
  
  for hostname, config in pairs(config.getconfig()) do
-       hosts[hostname] = make_host(hostname);
 -      hosts[hostname] = { events = prosody.events };
++      hosts[hostname] = { events = events };
  end
        
  require "core.modulemanager"
index 6e6f0949414b86d47ddf55794f0b4144aa64e0f0,f3e31a7f0d7f7783a1bc08d68c0588a864108664..7b5a5081f8d743b2891afa1b613cbbea4bd47c73
@@@ -20,16 -20,16 +20,6 @@@ module "anonymous
  
  --=========================
  --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
index 1a2ba01e406ac1a2b7f63f81fea28c1b3116e7f6,2abbc53a1688dd66c2d02253676a69446a5aac85..3982118299ac562ab2ccb5b02fe306aee4dd6992
@@@ -28,10 -28,15 +28,10 @@@ plain
                return password, state;
        end
  
 -plain-test:
 +plain_test:
-       function(username, password, realm)
+       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 self.profile.plain then
                local correct_password;
                correct_password, state = self.profile.plain(authentication, self.realm);
-               correct = (correct_password == password);
+               if correct_password == password then correct = true; else correct = false; end
        elseif self.profile.plain_test then
-               correct, state = self.profile.plain_test(authentication, password, self.realm);
+               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
index 63f6f2fe676fa1bf6d7d17d244c17726b059e3d3,ed7d7bc385a0de3fbfd33c8b4223c0deff1db26d..1340423cd932e3d3ae1117dd57bdb2ba80bd2e26
@@@ -32,10 -32,9 +32,10 @@@ module "scram
  --[[
  Supported Authentication Backends
  
 -scram-{MECH}:
 +scram_{MECH}:
 +      -- MECH being a standard hash name (like those at IANA's hash registry) with '-' replaced with '_'
        function(username, realm)
-               return stored_key, server_key, iteration_count, salt, state;
+               return salted_password, iteration_count, salt, state;
        end
  ]]
  
@@@ -93,23 -92,6 +93,24 @@@ local function validate_username(userna
        return username;
  end
  
- local function hashprep(hashname)
-       return hashname:lower():gsub("-", "_");
++local function hashprep( hashname ) 
++      local hash = hashname:lower()
++      hash = hash:gsub("-", "_")
++      return hash
 +end
 +
- function getAuthenticationDatabaseSHA1(password, salt, iteration_count)
++function saltedPasswordSHA1(password, salt, iteration_count)
++      local salted_password
 +      if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then
 +              return false, "inappropriate argument types"
 +      end
 +      if iteration_count < 4096 then
 +              log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.")
 +      end
-       local salted_password = Hi(hmac_sha1, password, salt, iteration_count);
-       local stored_key = sha1(hmac_sha1(salted_password, "Client Key"))
-       local server_key = hmac_sha1(salted_password, "Server Key");
-       return true, stored_key, server_key
++
++      return true, Hi(hmac_sha1, password, salt, iteration_count);
 +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
                                        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);
 +
 +                              local succ = false;
-                               succ, self.state.stored_key, self.state.server_key = getAuthenticationDatabaseSHA1(password, self.state.salt, default_i, self.state.iteration_count);
++                              succ, self.state.salted_password = saltedPasswordSHA1(password, self.state.salt, default_i, self.state.iteration_count);
 +                              if not succ then
-                                       log("error", "Generating authentication database failed. Reason: %s", self.state.stored_key);
++                                      log("error", "Generating salted password failed. Reason: %s", self.state.salted_password);
 +                                      return "failure", "temporary-auth-failure";
 +                              end
 +                      elseif self.profile["scram_"..hashprep(hash_name)] then
-                               local stored_key, server_key, iteration_count, salt, state = self.profile["scram_"..hashprep(hash_name)](self.state.name, self.realm);
++                              local salted_password, iteration_count, salt, state = self.profile["scram_"..hashprep(hash_name)](self.state.name, self.realm);
                                if state == nil then return "failure", "not-authorized"
                                elseif state == false then return "failure", "account-disabled" end
                                
                        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
 -              
 +
 +                      if self.state.nonce ~= self.state.clientnonce..self.state.servernonce then
 +                              return "failure", "malformed-request", "Wrong nonce in client-final-message.";
 +                      end
 +                      
-                       local ServerKey = self.state.server_key;
-                       local StoredKey = self.state.stored_key;
-                       
+                       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 ClientKey = binaryXOR(ClientSignature, base64.decode(self.state.proof))
+                       local ClientProof     = binaryXOR(ClientKey, ClientSignature)
                        local ServerSignature = HMAC_f(ServerKey, AuthMessage)
 -              
 +
-                       if StoredKey == H_f(ClientKey) then
+                       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;