From ee5a8c95433c8e183c537d936a37e4701e3997f3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 11:21:24 +0000 Subject: configmanager, hostmanager, prosody: Almost complete removal of section-related code, and the infamous 'core' section. Still backwards-compatible with API users. --- core/configmanager.lua | 77 ++++++++++++++++++++------------------------------ core/hostmanager.lua | 10 +++---- prosody | 4 +-- 3 files changed, 37 insertions(+), 54 deletions(-) diff --git a/core/configmanager.lua b/core/configmanager.lua index 51b9f5fe..a0a1f817 100644 --- a/core/configmanager.lua +++ b/core/configmanager.lua @@ -22,62 +22,48 @@ module "configmanager" local parsers = {}; local config_mt = { __index = function (t, k) return rawget(t, "*"); end}; -local config = setmetatable({ ["*"] = { core = {} } }, config_mt); +local config = setmetatable({ ["*"] = { } }, config_mt); -- When host not found, use global local host_mt = { }; --- When key not found in section, check key in global's section -function section_mt(section_name) - return { __index = function (t, k) - local section = rawget(config["*"], section_name); - if not section then return nil; end - return section[k]; - end - }; -end - function getconfig() return config; end -function get(host, section, key) - if not key then - section, key = "core", section; - end - local sec = config[host][section]; - if sec then - return sec[key]; +function get(host, key, _oldkey) + if key == "core" then + key = _oldkey; -- COMPAT with code that still uses "core" end - return nil; + return config[host][key]; end -function _M.rawget(host, section, key) +function _M.rawget(host, key, _oldkey) + if key == "core" then + key = _oldkey; -- COMPAT with code that still uses "core" + end local hostconfig = rawget(config, host); if hostconfig then - local sectionconfig = rawget(hostconfig, section); - if sectionconfig then - return rawget(sectionconfig, key); - end + return rawget(hostconfig, key); end end -local function set(config, host, section, key, value) - if host and section and key then +local function set(config, host, key, value) + if host and key then local hostconfig = rawget(config, host); if not hostconfig then hostconfig = rawset(config, host, setmetatable({}, host_mt))[host]; end - if not rawget(hostconfig, section) then - hostconfig[section] = setmetatable({}, section_mt(section)); - end - hostconfig[section][key] = value; + hostconfig[key] = value; return true; end return false; end -function _M.set(host, section, key, value) - return set(config, host, section, key, value); +function _M.set(host, key, value, _oldvalue) + if key == "core" then + key, value = value, _oldvalue; --COMPAT with code that still uses "core" + end + return set(config, host, key, value); end -- Helper function to resolve relative paths (needed by config) @@ -122,7 +108,7 @@ function load(filename, format) if parsers[format] and parsers[format].load then local f, err = io.open(filename); if f then - local new_config = setmetatable({ ["*"] = { core = {} } }, config_mt); + local new_config = setmetatable({ ["*"] = { } }, config_mt); local ok, err = parsers[format].load(f:read("*a"), filename, new_config); f:close(); if ok then @@ -176,53 +162,50 @@ do Component = true, component = true, Include = true, include = true, RunScript = true }, { __index = function (t, k) - return rawget(_G, k) or - function (settings_table) - config[__currenthost or "*"][k] = settings_table; - end; + return rawget(_G, k); end, __newindex = function (t, k, v) - set(config, env.__currenthost or "*", "core", k, v); + set(config, env.__currenthost or "*", k, v); end }); rawset(env, "__currenthost", "*") -- Default is global function env.VirtualHost(name) - if rawget(config, name) and rawget(config[name].core, "component_module") then + if rawget(config, name) and rawget(config[name], "component_module") then error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s", - name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0); + name, config[name].component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0); end rawset(env, "__currenthost", name); -- Needs at least one setting to logically exist :) - set(config, name or "*", "core", "defined", true); + set(config, name or "*", "defined", true); return function (config_options) rawset(env, "__currenthost", "*"); -- Return to global scope for option_name, option_value in pairs(config_options) do - set(config, name or "*", "core", option_name, option_value); + set(config, name or "*", option_name, option_value); end end; end env.Host, env.host = env.VirtualHost, env.VirtualHost; function env.Component(name) - if rawget(config, name) and rawget(config[name].core, "defined") and not rawget(config[name].core, "component_module") then + if rawget(config, name) and rawget(config[name], "defined") and not rawget(config[name], "component_module") then error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s", name, name, name), 0); end - set(config, name, "core", "component_module", "component"); + set(config, name, "component_module", "component"); -- Don't load the global modules by default - set(config, name, "core", "load_global_modules", false); + set(config, name, "load_global_modules", false); rawset(env, "__currenthost", name); local function handle_config_options(config_options) rawset(env, "__currenthost", "*"); -- Return to global scope for option_name, option_value in pairs(config_options) do - set(config, name or "*", "core", option_name, option_value); + set(config, name or "*", option_name, option_value); end end return function (module) if type(module) == "string" then - set(config, name, "core", "component_module", module); + set(config, name, "component_module", module); return handle_config_options; end return handle_config_options(module); diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 7c9298cd..52f494b5 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -37,8 +37,8 @@ local function load_enabled_hosts(config) local activated_any_host; for host, host_config in pairs(defined_hosts) do - if host ~= "*" and host_config.core.enabled ~= false then - if not host_config.core.component_module then + if host ~= "*" and host_config.enabled ~= false then + if not host_config.component_module then activated_any_host = true; end activate(host, host_config); @@ -78,7 +78,7 @@ function activate(host, host_config) send = host_send; modules = {}; }; - if not host_config.core.component_module then -- host + if not host_config.component_module then -- host host_session.type = "local"; host_session.sessions = {}; else -- component @@ -86,9 +86,9 @@ function activate(host, host_config) end hosts[host] = host_session; if not host:match("[@/]") then - disco_items:set(host:match("%.(.*)") or "*", host, host_config.core.name or true); + disco_items:set(host:match("%.(.*)") or "*", host, host_config.name or true); end - for option_name in pairs(host_config.core) do + for option_name in pairs(host_config) do if option_name:match("_ports$") or option_name:match("_interface$") then log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in the server-wide section instead", host, option_name); end diff --git a/prosody b/prosody index 00fde04c..481b3f06 100755 --- a/prosody +++ b/prosody @@ -132,8 +132,8 @@ end function sanity_check() for host, host_config in pairs(config.getconfig()) do if host ~= "*" - and host_config.core.enabled ~= false - and not host_config.core.component_module then + and host_config.enabled ~= false + and not host_config.component_module then return; end end -- cgit v1.2.3 From bf3eb41b5585e259aab85be16f999bb9a3074d59 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 11:22:50 +0000 Subject: prosody: Add COMPAT note about globals --- prosody | 1 + 1 file changed, 1 insertion(+) diff --git a/prosody b/prosody index 481b3f06..5802c348 100755 --- a/prosody +++ b/prosody @@ -198,6 +198,7 @@ function set_function_metatable() end function init_global_state() + -- COMPAT: These globals are deprecated bare_sessions = {}; full_sessions = {}; hosts = {}; -- cgit v1.2.3 From a2a4f805cd1882fee2d736094550325a9f72119b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 11:23:40 +0000 Subject: util.pposix: Allow fetching RLIMIT_NICE when available --- util-src/pposix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util-src/pposix.c b/util-src/pposix.c index 05303d99..99a308cf 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -483,6 +483,9 @@ int string2resource(const char *s) { if (!strcmp(s, "MEMLOCK")) return RLIMIT_MEMLOCK; if (!strcmp(s, "NPROC")) return RLIMIT_NPROC; if (!strcmp(s, "RSS")) return RLIMIT_RSS; +#endif +#ifdef RLIMIT_NICE + if (!strcmp(s, "NICE")) return RLIMIT_NICE; #endif return -1; } -- cgit v1.2.3 From 3ba555b9c5e925d138ee4a287f242f2c748f7100 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 11:24:13 +0000 Subject: hostmanager: Use prosody.hosts instead of 'hosts' global --- core/hostmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 52f494b5..800f7b34 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -17,7 +17,7 @@ local uuid_gen = require "util.uuid".generate; local log = require "util.logger".init("hostmanager"); -local hosts = hosts; +local hosts = prosody.hosts; local prosody_events = prosody.events; if not _G.prosody.incoming_s2s then require "core.s2smanager"; -- cgit v1.2.3 From 77431e5d84383491a5ee673ce38f4912bd26c98b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 11:24:54 +0000 Subject: hostmanager: Use rawget to check for activated hosts to prevent recursion in dynamic host loaders --- core/hostmanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 800f7b34..40401e48 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -25,7 +25,7 @@ end local incoming_s2s = _G.prosody.incoming_s2s; local core_route_stanza = _G.prosody.core_route_stanza; -local pairs, select = pairs, select; +local pairs, select, rawget = pairs, select, rawget; local tostring, type = tostring, type; module "hostmanager" @@ -67,7 +67,7 @@ local function host_send(stanza) end function activate(host, host_config) - if hosts[host] then return nil, "The host "..host.." is already activated"; end + if rawget(hosts, host) then return nil, "The host "..host.." is already activated"; end host_config = host_config or configmanager.getconfig()[host]; if not host_config then return nil, "Couldn't find the host "..tostring(host).." defined in the current config"; end local host_session = { -- cgit v1.2.3 From 339e74b1b9961f14dc289656973997d6583771d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 14:18:23 +0000 Subject: s2smanager, mod_s2s, mod_dialback, mod_saslauth: Move s2smanager.make_authenticated() to mod_s2s, and plugins now signal authentication via the s2s-authenticated event --- core/s2smanager.lua | 75 ++------------------------------------------ plugins/mod_dialback.lua | 5 ++- plugins/mod_s2s/mod_s2s.lua | 76 +++++++++++++++++++++++++++++++++++++++++++-- plugins/mod_saslauth.lua | 5 ++- 4 files changed, 80 insertions(+), 81 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 86389d8d..b034fcae 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,15 +9,13 @@ local hosts = hosts; -local tostring, pairs, ipairs, getmetatable, newproxy, setmetatable - = tostring, pairs, ipairs, getmetatable, newproxy, setmetatable; +local tostring, pairs, getmetatable, newproxy, setmetatable + = tostring, pairs, getmetatable, newproxy, setmetatable; local logger_init = require "util.logger".init; local log = logger_init("s2smanager"); -local config = require "core.configmanager"; - local prosody = _G.prosody; incoming_s2s = {}; prosody.incoming_s2s = incoming_s2s; @@ -49,75 +47,6 @@ function new_outgoing(from_host, to_host, connect) return host_session; end -function make_authenticated(session, host) - if not session.secure then - local local_host = session.direction == "incoming" and session.to_host or session.from_host; - if config.get(local_host, "core", "s2s_require_encryption") then - session:close({ - condition = "policy-violation", - text = "Encrypted server-to-server communication is required but was not " - ..((session.direction == "outgoing" and "offered") or "used") - }); - end - end - if session.type == "s2sout_unauthed" then - session.type = "s2sout"; - elseif session.type == "s2sin_unauthed" then - session.type = "s2sin"; - if host then - if not session.hosts[host] then session.hosts[host] = {}; end - session.hosts[host].authed = true; - end - elseif session.type == "s2sin" and host then - if not session.hosts[host] then session.hosts[host] = {}; end - session.hosts[host].authed = true; - else - return false; - end - session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); - - mark_connected(session); - - return true; -end - --- Stream is authorised, and ready for normal stanzas -function mark_connected(session) - local sendq, send = session.sendq, session.sends2s; - - local from, to = session.from_host, session.to_host; - - session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to); - - local event_data = { session = session }; - if session.type == "s2sout" then - fire_event("s2sout-established", event_data); - hosts[from].events.fire_event("s2sout-established", event_data); - else - local host_session = hosts[to]; - session.send = function(stanza) - return host_session.events.fire_event("route/remote", { from_host = to, to_host = from, stanza = stanza }); - end; - - fire_event("s2sin-established", event_data); - hosts[to].events.fire_event("s2sin-established", event_data); - end - - if session.direction == "outgoing" then - if sendq then - session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); - for i, data in ipairs(sendq) do - send(data[1]); - sendq[i] = nil; - end - session.sendq = nil; - end - - session.ip_hosts = nil; - session.srv_hosts = nil; - end -end - local resting_session = { -- Resting, not dead destroyed = true; type = "s2s_destroyed"; diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua index 34d8a2fb..9dcb0ed5 100644 --- a/plugins/mod_dialback.lua +++ b/plugins/mod_dialback.lua @@ -7,7 +7,6 @@ -- local hosts = _G.hosts; -local s2s_make_authenticated = require "core.s2smanager".make_authenticated; local log = module._log; @@ -110,7 +109,7 @@ module:hook("stanza/jabber:server:dialback:verify", function(event) if dialback_verifying and attr.from == origin.to_host then local valid; if attr.type == "valid" then - s2s_make_authenticated(dialback_verifying, attr.from); + module:fire_event("s2s-authenticated", { session = dialback_verifying, host = attr.from }); valid = "valid"; else -- Warn the original connection that is was not verified successfully @@ -146,7 +145,7 @@ module:hook("stanza/jabber:server:dialback:result", function(event) return true; end if stanza.attr.type == "valid" then - s2s_make_authenticated(origin, attr.from); + module:fire_event("s2s-authenticated", { session = origin, host = attr.from }); else origin:close("not-authorized", "dialback authentication failed"); end diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 8d99b855..fb54188a 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -24,15 +24,17 @@ local new_xmpp_stream = require "util.xmppstream".new; local s2s_new_incoming = require "core.s2smanager".new_incoming; local s2s_new_outgoing = require "core.s2smanager".new_outgoing; local s2s_destroy_session = require "core.s2smanager".destroy_session; -local s2s_mark_connected = require "core.s2smanager".mark_connected; local uuid_gen = require "util.uuid".generate; local cert_verify_identity = require "util.x509".verify_identity; +local fire_global_event = prosody.events.fire_event; local s2sout = module:require("s2sout"); local connect_timeout = module:get_option_number("s2s_timeout", 90); local stream_close_timeout = module:get_option_number("s2s_close_timeout", 5); +local require_encryption = module:get_option_boolean("s2s_require_encryption", secure_auth); + local sessions = module:shared("sessions"); local log = module._log; @@ -132,6 +134,76 @@ function module.add_host(module) end module:hook("route/remote", route_to_existing_session, 200); module:hook("route/remote", route_to_new_session, 100); + module:hook("s2s-authenticated", make_authenticated, -1); +end + +-- Stream is authorised, and ready for normal stanzas +function mark_connected(session) + local sendq, send = session.sendq, session.sends2s; + + local from, to = session.from_host, session.to_host; + + session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to); + + local event_data = { session = session }; + if session.type == "s2sout" then + fire_global_event("s2sout-established", event_data); + hosts[from].events.fire_event("s2sout-established", event_data); + else + local host_session = hosts[to]; + session.send = function(stanza) + return host_session.events.fire_event("route/remote", { from_host = to, to_host = from, stanza = stanza }); + end; + + fire_global_event("s2sin-established", event_data); + hosts[to].events.fire_event("s2sin-established", event_data); + end + + if session.direction == "outgoing" then + if sendq then + session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); + for i, data in ipairs(sendq) do + send(data[1]); + sendq[i] = nil; + end + session.sendq = nil; + end + + session.ip_hosts = nil; + session.srv_hosts = nil; + end +end + +function make_authenticated(event) + local session, host = event.session, event.host; + if not session.secure then + if require_encryption or secure_auth or secure_domains[host] then + session:close({ + condition = "policy-violation", + text = "Encrypted server-to-server communication is required but was not " + ..((session.direction == "outgoing" and "offered") or "used") + }); + end + end + if session.type == "s2sout_unauthed" then + session.type = "s2sout"; + elseif session.type == "s2sin_unauthed" then + session.type = "s2sin"; + if host then + if not session.hosts[host] then session.hosts[host] = {}; end + session.hosts[host].authed = true; + end + elseif session.type == "s2sin" and host then + if not session.hosts[host] then session.hosts[host] = {}; end + session.hosts[host].authed = true; + else + return false; + end + session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); + + mark_connected(session); + + return true; end --- Helper to check that a session peer's certificate is valid @@ -287,7 +359,7 @@ function stream_callbacks.streamopened(session, attr) if not session.dialback_verifying then hosts[session.from_host].events.fire_event("s2sout-authenticate-legacy", { origin = session }); else - s2s_mark_connected(session); + mark_connected(session); end end end diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 264ee967..b75b1844 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -11,7 +11,6 @@ local st = require "util.stanza"; local sm_bind_resource = require "core.sessionmanager".bind_resource; local sm_make_authenticated = require "core.sessionmanager".make_authenticated; -local s2s_make_authenticated = require "core.s2smanager".make_authenticated; local base64 = require "util.encodings".base64; local cert_verify_identity = require "util.x509".verify_identity; @@ -90,7 +89,7 @@ module:hook_stanza(xmlns_sasl, "success", function (session, stanza) session:reset_stream(); session:open_stream(); - s2s_make_authenticated(session, session.to_host); + module:fire_event("s2s-authenticated", { session = session, host = session.to_host }); return true; end) @@ -187,7 +186,7 @@ local function s2s_external_auth(session, stanza) local domain = text ~= "" and text or session.from_host; module:log("info", "Accepting SASL EXTERNAL identity from %s", domain); - s2s_make_authenticated(session, domain); + module:fire_event("s2s-authenticated", { session = session, host = domain }); session:reset_stream(); return true end -- cgit v1.2.3 From b8efb428ea0ddea38732edc873281552ffe592e5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 22 Mar 2013 14:21:02 +0000 Subject: mod_s2s: Add controls for certificate validation via the s2s_secure_auth option. Plugins can now return false from handling s2s-check-certificate to prevent connection establishment (s2sin+s2sout) --- plugins/mod_s2s/mod_s2s.lua | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index fb54188a..512c9037 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -33,6 +33,9 @@ local s2sout = module:require("s2sout"); local connect_timeout = module:get_option_number("s2s_timeout", 90); local stream_close_timeout = module:get_option_number("s2s_close_timeout", 5); +local secure_auth = module:get_option_boolean("s2s_secure_auth", false); -- One day... +local secure_domains, insecure_domains = + module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items; local require_encryption = module:get_option_boolean("s2s_require_encryption", secure_auth); local sessions = module:shared("sessions"); @@ -239,7 +242,7 @@ local function check_cert_status(session) end end end - module:fire_event("s2s-check-certificate", { host = host, session = session, cert = cert }); + return module:fire_event("s2s-check-certificate", { host = host, session = session, cert = cert }); end --- XMPP stream event handlers @@ -318,7 +321,11 @@ function stream_callbacks.streamopened(session, attr) end end - if session.secure and not session.cert_chain_status then check_cert_status(session); end + if session.secure and not session.cert_chain_status then + if check_cert_status(session) == false then + return; + end + end session:open_stream() if session.version >= 1.0 then @@ -338,7 +345,11 @@ function stream_callbacks.streamopened(session, attr) if not attr.id then error("stream response did not give us a streamid!!!"); end session.streamid = attr.id; - if session.secure and not session.cert_chain_status then check_cert_status(session); end + if session.secure and not session.cert_chain_status then + if check_cert_status(session) == false then + return; + end + end -- Send unauthed buffer -- (stanzas which are fine to send before dialback) @@ -598,6 +609,24 @@ function listener.register_outgoing(conn, session) initialize_session(session); end +function check_auth_policy(event) + local host, session = event.host, event.session; + + if not secure_auth and secure_domains[host] then + secure_auth = true; + elseif secure_auth and insecure_domains[host] then + secure_auth = false; + end + + if secure_auth and not session.cert_identity_status then + module:log("warn", "Forbidding insecure connection to/from %s", host); + session:close(false); + return false; + end +end + +module:hook("s2s-check-certificate", check_auth_policy, -1); + s2sout.set_listener(listener); module:hook("server-stopping", function(event) -- cgit v1.2.3