Mercurial > prosody-modules
view mod_auth_external_insecure/mod_auth_external_insecure.lua @ 4203:c4002aae4ad3
mod_s2s_keepalive: Use timestamp as iq @id
RFC 6120 implies that the id attribute must be unique within a stream.
This should fix problems with remote servers that enforce uniqueness and
don't answer duplicated ids.
If it doesn't do that, then at least you can get a guesstimate at
round-trip time from the difference between the result iq stanza and the
timestamp it was logged without having to go look for when it was sent,
or needing to keep state.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 14 Oct 2020 18:02:10 +0200 |
parents | f84ede3e9e3b |
children |
line wrap: on
line source
-- -- Prosody IM -- Copyright (C) 2010 Waqas Hussain -- Copyright (C) 2010 Jeff Mitchell -- Copyright (C) 2013 Mikael Nordfeldth -- Copyright (C) 2013 Matthew Wild, finally came to fix it all -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- local lpty = assert(require "lpty", "mod_auth_external requires lpty: https://modules.prosody.im/mod_auth_external.html#installation"); local usermanager = require "core.usermanager"; local new_sasl = require "util.sasl".new; local server = require "net.server"; local have_async, async = pcall(require, "util.async"); local log = module._log; local host = module.host; local script_type = module:get_option_string("external_auth_protocol", "generic"); local command = module:get_option_string("external_auth_command", ""); local read_timeout = module:get_option_number("external_auth_timeout", 5); local blocking = module:get_option_boolean("external_auth_blocking", true); -- non-blocking is very experimental local auth_processes = module:get_option_number("external_auth_processes", 1); assert(script_type == "ejabberd" or script_type == "generic", "Config error: external_auth_protocol must be 'ejabberd' or 'generic'"); assert(not host:find(":"), "Invalid hostname"); if not blocking then assert(server.event, "External auth non-blocking mode requires libevent installed and enabled"); log("debug", "External auth in non-blocking mode, yay!") waiter, guard = async.waiter, async.guarder(); elseif auth_processes > 1 then log("warn", "external_auth_processes is greater than 1, but we are in blocking mode - reducing to 1"); auth_processes = 1; end local ptys = {}; local pty_options = { throw_errors = false, no_local_echo = true, use_path = false }; for i = 1, auth_processes do ptys[i] = lpty.new(pty_options); end function module.unload() for i = 1, auth_processes do ptys[i]:endproc(); end end module:hook_global("server-cleanup", module.unload); local curr_process = 0; function send_query(text) curr_process = (curr_process%auth_processes)+1; local pty = ptys[curr_process]; local finished_with_pty if not blocking then finished_with_pty = guard(pty); -- Prevent others from crossing this line while we're busy end if not pty:hasproc() then local status, ret = pty:exitstatus(); if status and (status ~= "exit" or ret ~= 0) then log("warn", "Auth process exited unexpectedly with %s %d, restarting", status, ret or 0); return nil; end local ok, err = pty:startproc(command); if not ok then log("error", "Failed to start auth process '%s': %s", command, err); return nil; end log("debug", "Started auth process"); end pty:send(text); if blocking then return pty:read(read_timeout); else local response; local wait, done = waiter(); server.addevent(pty:getfd(), server.event.EV_READ, function () response = pty:read(); done(); return -1; end); wait(); finished_with_pty(); return response; end end function do_query(kind, username, password) if not username then return nil, "not-acceptable"; end local query = (password and "%s:%s:%s:%s" or "%s:%s:%s"):format(kind, username, host, password); local len = #query if len > 1000 then return nil, "policy-violation"; end if script_type == "ejabberd" then local lo = len % 256; local hi = (len - lo) / 256; query = string.char(hi, lo)..query; elseif script_type == "generic" then query = query..'\n'; end local response, err = send_query(query); if not response then log("warn", "Error while waiting for result from auth process: %s", err or "unknown error"); elseif (script_type == "ejabberd" and response == "\0\2\0\0") or (script_type == "generic" and response:gsub("\r?\n$", "") == "0") then return nil, "not-authorized"; elseif (script_type == "ejabberd" and response == "\0\2\0\1") or (script_type == "generic" and response:gsub("\r?\n$", "") == "1") then return true; else log("warn", "Unable to interpret data from auth process, %s", (response:match("^error:") and response) or ("["..#response.." bytes]")); return nil, "internal-server-error"; end end local provider = {}; function provider.test_password(username, password) return do_query("auth", username, password); end function provider.set_password(username, password) return do_query("setpass", username, password); end function provider.user_exists(username) return do_query("isuser", username); end function provider.create_user(username, password) -- luacheck: ignore 212 return nil, "Account creation/modification not available."; end function provider.get_sasl_handler() local testpass_authentication_profile = { plain_test = function(sasl, username, password, realm) return usermanager.test_password(username, realm, password), true; end, }; return new_sasl(host, testpass_authentication_profile); end module:provides("auth", provider);