Software /
code /
prosody-modules
File
mod_auth_wordpress/mod_auth_wordpress.lua @ 4930:13070c6a7ce8
mod_http_muc_log: Fix exception on lack of trailing slash in room path
A request to /room leads to the match call returning nil which in turn
calls nodeprep(nil). In Prosody 0.11.x this does nothing and simply
returns the nil, while in 0.12 it is an error.
Now it redirects to the calendar view at /room/ - even for non-existant
rooms.
Discovered at a deployment with http_paths = { muc_log = "/" } and
requests to /robots.txt and similar, which now result in a uses redirect
before returning 404.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 22 Apr 2022 14:29:32 +0200 |
parent | 1343:7dbde05b48a9 |
line wrap: on
line source
-- Wordpress authentication backend for Prosody -- -- Copyright (C) 2011 Waqas Hussain -- Copyright (C) 2011 Kim Alvefur -- local log = require "util.logger".init("auth_sql"); local new_sasl = require "util.sasl".new; local nodeprep = require "util.encodings".stringprep.nodeprep; local saslprep = require "util.encodings".stringprep.saslprep; local DBI = require "DBI" local md5 = require "util.hashes".md5; local uuid_gen = require "util.uuid".generate; local connection; local params = module:get_option("sql"); local table_prefix = module:get_option_string("wordpress_table_prefix", "wp_"); local resolve_relative_path = require "core.configmanager".resolve_relative_path; local function test_connection() if not connection then return nil; end if connection:ping() then return true; else module:log("debug", "Database connection closed"); connection = nil; end end local function connect() if not test_connection() then prosody.unlock_globals(); local dbh, err = DBI.Connect( params.driver, params.database, params.username, params.password, params.host, params.port ); prosody.lock_globals(); if not dbh then module:log("debug", "Database connection failed: %s", tostring(err)); return nil, err; end module:log("debug", "Successfully connected to database"); dbh:autocommit(true); -- don't run in transaction connection = dbh; return connection; end end do -- process options to get a db connection params = params or { driver = "SQLite3" }; if params.driver == "SQLite3" then params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); end assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); assert(connect()); end local function getsql(sql, ...) if params.driver == "PostgreSQL" then sql = sql:gsub("`", "\""); end if not test_connection() then connect(); end -- do prepared statement stuff local stmt, err = connection:prepare(sql); if not stmt and not test_connection() then error("connection failed"); end if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end -- run query local ok, err = stmt:execute(...); if not ok and not test_connection() then error("connection failed"); end if not ok then return nil, err; end return stmt; end local function setsql(sql, ...) local stmt, err = getsql(sql, ...); if not stmt then return stmt, err; end return stmt:affected(); end local function get_password(username) local stmt, err = getsql("SELECT `user_pass` FROM `"..table_prefix.."users` WHERE `user_login`=?", username); if stmt then for row in stmt:rows(true) do return row.user_pass; end end end local itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; local function hashEncode64(input, count) local output = ""; local i, value = 0, 0; while true do value = input:byte(i+1) i = i+1; local idx = value % 0x40 + 1; output = output .. itoa64:sub(idx, idx); if i < count then value = value + input:byte(i+1) * 256; end local _ = value % (2^6); local idx = ((value - _) / (2^6)) % 0x40 + 1 output = output .. itoa64:sub(idx, idx); if i >= count then break; end i = i+1; if i < count then value = value + input:byte(i+1) * 256 * 256; end local _ = value % (2^12); local idx = ((value - _) / (2^12)) % 0x40 + 1 output = output .. itoa64:sub(idx, idx); if i >= count then break; end i = i+1; local _ = value % (2^18); local idx = ((value - _) / (2^18)) % 0x40 + 1 output = output .. itoa64:sub(idx, idx); if not(i < count) then break; end end return output; end local function hashCryptPrivate(password, genSalt) local output = "*"; if not genSalt:match("^%$P%$") then return output; end local count_log2 = itoa64:find(genSalt:sub(4,4)) - 1; if count_log2 < 7 or count_log2 > 30 then return output; end local count = 2 ^ count_log2; local salt = genSalt:sub(5, 12); if #salt ~= 8 then return output; end local hash = md5(salt..password); while true do hash = md5(hash..password); if not(count > 1) then break; end count = count-1; end output = genSalt:sub(1, 12); output = output .. hashEncode64(hash, 16); return output; end local function hashGensaltPrivate(input) local iteration_count_log2 = 6; local output = "$P$"; local idx = math.min(iteration_count_log2 + 5, 30) + 1; output = output .. itoa64:sub(idx, idx); output = output .. hashEncode64(input, 6); return output; end local function wordpressCheckHash(password, hash) if #hash == 32 then return hash == md5(password, true); end return #hash == 34 and hashCryptPrivate(password, hash) == hash; end local function wordpressCreateHash(password) local random = uuid_gen():sub(-6); local salt = hashGensaltPrivate(random); local hash = hashCryptPrivate(password, salt); if #hash == 34 then return hash; end return md5(password, true); end provider = {}; function provider.test_password(username, password) local hash = get_password(username); return hash and wordpressCheckHash(password, hash); end function provider.user_exists(username) module:log("debug", "test user %s existence", username); return get_password(username) and true; end function provider.get_password(username) return nil, "Getting password is not supported."; end function provider.set_password(username, password) local hash = wordpressCreateHash(password); local stmt, err = setsql("UPDATE `"..table_prefix.."users` SET `user_pass`=? WHERE `user_login`=?", hash, username); return stmt and true, err; end function provider.create_user(username, password) return nil, "Account creation/modification not supported."; end local escapes = { [" "] = "\\20"; ['"'] = "\\22"; ["&"] = "\\26"; ["'"] = "\\27"; ["/"] = "\\2f"; [":"] = "\\3a"; ["<"] = "\\3c"; [">"] = "\\3e"; ["@"] = "\\40"; ["\\"] = "\\5c"; }; local unescapes = {}; for k,v in pairs(escapes) do unescapes[v] = k; end local function jid_escape(s) return s and (s:gsub(".", escapes)); end local function jid_unescape(s) return s and (s:gsub("\\%x%x", unescapes)); end function provider.get_sasl_handler() local sasl = {}; function sasl:clean_clone() return provider.get_sasl_handler(); end function sasl:mechanisms() return { PLAIN = true; }; end function sasl:select(mechanism) if not self.selected and mechanism == "PLAIN" then self.selected = mechanism; return true; end end function sasl:process(message) if not message then return "failure", "malformed-request"; end local authorization, authentication, password = message:match("^([^%z]*)%z([^%z]+)%z([^%z]+)"); if not authorization then return "failure", "malformed-request"; end authentication = saslprep(authentication); password = saslprep(password); if (not password) or (password == "") or (not authentication) or (authentication == "") then return "failure", "malformed-request", "Invalid username or password."; end local function test(authentication) local prepped = nodeprep(authentication); local normalized = jid_unescape(prepped); return normalized and provider.test_password(normalized, password) and prepped; end local username = test(authentication) or test(jid_escape(authentication)); if username then self.username = username; return "success"; end return "failure", "not-authorized", "Unable to authorize you with the authentication credentials you've sent."; end return sasl; end module:provides("auth", provider);