Mercurial > prosody-modules
view mod_auth_external_insecure/mod_auth_external_insecure.lua @ 5406:b86d80e21c60
mod_http_oauth2: Validate consistency of response and grant types
Ensure that these correlated fields make sense per RFC 7591 ยง 2.1, even
though we currently only check the response type during authorization.
This could probably all be deleted if (when!) we remove the implicit
grant, since then these things don't make any sense anymore.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Tue, 02 May 2023 16:34:31 +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);