Mercurial > prosody-modules
view mod_net_dovecotauth/mod_net_dovecotauth.lua @ 4049:78f1de5301fc
mod_adhoc_dataforms_demo: Fix duplicate field prevention
It's supposed to only include each type of form field once per form at
most but it didn't note which fields it had added already. No idea what
the probability was anyways, probably pretty low.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Tue, 23 Jun 2020 19:40:55 +0200 |
parents | 8e686bf63441 |
children |
line wrap: on
line source
-- mod_net_dovecotauth.lua -- -- Protocol spec: -- http://dovecot.org/doc/auth-protocol.txt -- -- Example postfix config: -- sudo postconf smtpd_sasl_path=inet:127.0.0.1:28484 -- sudo postconf smtpd_sasl_type=dovecot -- sudo postconf smtpd_sasl_auth_enable=yes module:set_global(); -- Imports local new_sasl = require "core.usermanager".get_sasl_handler; local user_exists = require "core.usermanager".user_exists; local base64 = require"util.encodings".base64; local dump = require"util.serialization".serialize; local pposix = require "util.pposix"; -- Config local default_vhost = module:get_option_string("dovecotauth_host", (next(hosts))); -- TODO Is there a better solution? local allow_master = module:get_option_boolean("dovecotauth_allow_master", false); -- Active sessions local sessions = {}; -- Session methods local new_session; do local sess = { }; local sess_mt = { __index = sess }; function new_session(conn) local s = { type = "?", conn = conn, buf = "", sasl = {} } function s:log(l, m, ...) return module:log(l, self.type..tonumber(tostring(self):match("%x+$"), 16)..": "..m, ...); end return setmetatable(s, sess_mt); end function sess:send(...) local data = table.concat({...}, "\t") .. "\n" -- self:log("debug", "SEND: %s", dump(ret)); return self.conn:write(data); end local mech_params = { ANONYMOUS = "anonymous"; PLAIN = "plaintext"; ["DIGEST-MD5"] = "mutual-auth"; ["SCRAM-SHA-1"] = "mutual-auth"; ["SCRAM-SHA-1-PLUS"] = "mutual-auth"; } function sess:handshake() self:send("VERSION", 1, 1); self:send("SPID", pposix.getpid()); self:send("CUID", tonumber(tostring(self):match"%x+$", 16)); for mech in pairs(self.g_sasl:mechanisms()) do self:send("MECH", mech, mech_params[mech]); end self:send("DONE"); end function sess:feed(data) -- TODO break this up a bit -- module:log("debug", "sess = %s", dump(self)); local buf = self.buf; buf = buf .. data; local line, eol = buf:match("(.-)\r?\n()") while line and line ~= "" do buf = buf:sub(eol); self.buf = buf; local part = line:gmatch("[^\t]+"); local command = part(); if command == "VERSION" then local major = tonumber(part()); local minor = tonumber(part()); if major ~= 1 then self:log("warn", "Wrong version, expected 1.1, got %s.%s", tostring(major), tostring(minor)); self.conn:close(); break; end elseif command == "CPID" then self.type = "C"; self.pid = part(); elseif command == "SPID" and allow_master then self.type = "M"; self.pid = part(); elseif command == "AUTH" and self.type ~= "?" then -- C: "AUTH" TAB <id> TAB <mechanism> TAB service=<service> [TAB <parameters>] local id = part() -- <id> local sasl = self.sasl[id]; local mech = part(); if not sasl then -- TODO Should maybe initialize SASL handler after parsing the line? sasl = self.g_sasl:clean_clone(); self.sasl[id] = sasl; if not sasl:select(mech) then self:send("FAIL", id, "reason=invalid-mechanism"); self.sasl[id] = nil; sasl = false end end if sasl then local params = {}; -- Not used for anything yet for p in part do local k,v = p:match("^([^=]*)=(.*)$"); if k == "resp" then self:log("debug", "params = %s", dump(params)); v = base64.decode(v); local status, ret, err = sasl:process(v); self:log("debug", status); if status == "challenge" then self:send("CONT", id, base64.encode(ret)); elseif status == "failure" then self.sasl[id] = nil; self:send("FAIL", id, "reason="..tostring(err)); elseif status == "success" then self.sasl[id] = nil; self:send("OK", id, "user="..sasl.username, ret and "resp="..base64.encode(ret)); end break; -- resp MUST be the last param else params[k or p] = v or true; end end end elseif command == "USER" and self.type == "M" then -- FIXME Should this be on a separate listener? local id = part(); local user = part(); if user and user_exists(user, default_vhost) then self:send("USER", id); else self:send("NOTFOUND", id); end else self:log("warn", "Unhandled command %s", tostring(command)); self.conn:close(); break; end line, eol = buf:match("(.-)\r?\n()") end end end local listener = {} function listener.onconnect(conn) local s = new_session(conn); sessions[conn] = s; local g_sasl = new_sasl(default_vhost, s); s.g_sasl = g_sasl; s:handshake(); end function listener.onincoming(conn, data) local s = sessions[conn]; -- s:log("debug", "RECV %s", dump(data)); return s:feed(data); end function listener.ondisconnect(conn) sessions[conn] = nil; end function module.unload() for c in pairs(sessions) do c:close(); end end module:provides("net", { default_port = 28484; listener = listener; });